/*-------------------------------------------------------------------------
 *
 * parse_coerce.c
 *		处理解析器的类型强制/转换
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/parser/parse_coerce.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/pg_cast.h"
#include "catalog/pg_class.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_coerce.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/datum.h"		/* needed for datumIsEqual() */
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/typcache.h"


static Node *fc_coerce_type_typmod(Node *fc_node,
								Oid fc_targetTypeId, int32 fc_targetTypMod,
								CoercionContext fc_ccontext, CoercionForm fc_cformat,
								int fc_location,
								bool fc_hideInputCoercion);
static void fc_hide_coercion_node(Node *fc_node);
static Node *fc_build_coercion_expression(Node *fc_node,
									   CoercionPathType fc_pathtype,
									   Oid fc_funcId,
									   Oid fc_targetTypeId, int32 fc_targetTypMod,
									   CoercionContext fc_ccontext, CoercionForm fc_cformat,
									   int fc_location);
static Node *fc_coerce_record_to_complex(ParseState *fc_pstate, Node *fc_node,
									  Oid fc_targetTypeId,
									  CoercionContext fc_ccontext,
									  CoercionForm fc_cformat,
									  int fc_location);
static bool fc_is_complex_array(Oid fc_typid);
static bool fc_typeIsOfTypedTable(Oid fc_reltypeId, Oid fc_reloftypeId);


/*
 * coerce_to_target_type()
 *		将表达式转换为目标类型及 typmod。
 *
 * 这是任意类型强制转换操作的一般目的入口点。直接使用组件操作 can_coerce_type、coerce_type 和 coerce_type_typmod 应限制在特殊情况下（例如，当预期转换将成功时）。
 *
 * 返回可能已转换的表达式树，如果无法进行类型转换则返回 NULL。 （我们这样做，而不是直接进行 ereport，以便调用者可以生成自定义错误消息以指示上下文。）
 *
 * pstate - 解析状态（可以为 NULL，见 coerce_type）
 * expr - 输入表达式树（已由 transformExpr 转换）
 * exprtype - expr 的结果类型
 * targettype - 期望的结果类型
 * targettypmod - 期望的结果 typmod
 * ccontext, cformat - 控制强制转换的上下文指示符
 * location - 强制转换请求的解析位置，或 -1 如果未知/隐式
 */
Node * coerce_to_target_type(ParseState *fc_pstate, Node *fc_expr, Oid fc_exprtype,
					  Oid fc_targettype, int32 fc_targettypmod,
					  CoercionContext fc_ccontext,
					  CoercionForm fc_cformat,
					  int fc_location)
{
	Node	   *fc_result;
	Node	   *fc_origexpr;

	if (!can_coerce_type(1, &fc_exprtype, &fc_targettype, fc_ccontext))
		return NULL;

	/*
	 * 如果输入的顶部有 CollateExpr，则将其剥离，进行强制转换，然后再放回一个新的。 这很麻烦，因为它重复了 coerce_type 中的逻辑，但如果不这样做，就很难判断 coerce_type 是否实际上改变了任何内容，而我们必须知道这一点，以避免可能对不是由 coerce_type 生成的内容调用 hide_coercion_node。 请注意，如果有多个堆叠的 CollateExpr，我们只丢弃最顶部的。 此外，如果目标类型不是可比较的，我们将丢弃 CollateExpr。
	 */
	fc_origexpr = fc_expr;
	while (fc_expr && IsA(fc_expr, CollateExpr))
		fc_expr = (Node *) ((CollateExpr *) fc_expr)->arg;

	fc_result = coerce_type(fc_pstate, fc_expr, fc_exprtype,
						 fc_targettype, fc_targettypmod,
						 fc_ccontext, fc_cformat, fc_location);

	/*
	 * 如果目标是固定长度类型，可能需要长度强制转换以及类型强制转换。 如果我们发现同时添加这两者，则强制内部强制转换节点为隐式显示格式。
	 */
	fc_result = fc_coerce_type_typmod(fc_result,
								fc_targettype, fc_targettypmod,
								fc_ccontext, fc_cformat, fc_location,
								(fc_result != fc_expr && !IsA(fc_result, Const)));

	if (fc_expr != fc_origexpr && type_is_collatable(fc_targettype))
	{
		/* 重新安装顶部 CollateExpr */
		CollateExpr *fc_coll = (CollateExpr *) fc_origexpr;
		CollateExpr *fc_newcoll = makeNode(CollateExpr);

		fc_newcoll->arg = (Expr *) fc_result;
		fc_newcoll->collOid = fc_coll->collOid;
		fc_newcoll->location = fc_coll->location;
		fc_result = (Node *) fc_newcoll;
	}

	return fc_result;
}


/*
 * coerce_type()
 *		将表达式转换为不同的类型。
 *
 * 调用者应已确定强制转换是可能的； 请参见 can_coerce_type。
 *
 * 通常，此处不执行对 typmod（长度）的强制转换。 如果希望约束 typmod，调用者必须同时调用 coerce_type_typmod。 （但如果目标类型是一个域，它可能在内部包含一个 typmod 约束，该约束将在 coerce_to_domain 内部应用。）在某些情况下 pg_cast 指定了一个类型强制转换函数，该函数还适用长度转换，并且在仅这些情况下，结果将已正确强制转换为指定的 typmod。
 *
 * pstate 仅在能够解析先前 UNKNOWN Param 的类型时使用。 如果调用者不想更新 Params 的类型信息，传递 pstate = NULL 是可以的。
 *
 * 注意：此函数不得修改给定的表达式树，仅在其顶部添加装饰。 例如，请参阅 transformSetOperationTree。
 */
Node * coerce_type(ParseState *fc_pstate, Node *fc_node,
			Oid fc_inputTypeId, Oid fc_targetTypeId, int32 fc_targetTypeMod,
			CoercionContext fc_ccontext, CoercionForm fc_cformat, int fc_location)
{
	Node	   *fc_result;
	CoercionPathType fc_pathtype;
	Oid			fc_funcId;

	if (fc_targetTypeId == fc_inputTypeId ||
		fc_node == NULL)
	{
		/* 无需转换 */
		return fc_node;
	}
	if (fc_targetTypeId == ANYOID ||
		fc_targetTypeId == ANYELEMENTOID ||
		fc_targetTypeId == ANYNONARRAYOID ||
		fc_targetTypeId == ANYCOMPATIBLEOID ||
		fc_targetTypeId == ANYCOMPATIBLENONARRAYOID)
	{
		/*
		 * 假设 can_coerce_type 验证隐式转换是可以的。
		 *
		 * 注意：通过返回未修改的节点，我们表明将 UNKNOWN 常量视为接受这些伪类型之一的函数的有效输入是可以的。 这应该没问题，因为 UNKNOWN 值仍然是一个完全有效的 Datum。
		 *
		 * 注意：我们在这里不希望一个 RelabelType：函数参数的公开类型必须是其实际类型，而不是多态伪类型。
		 */
		return fc_node;
	}
	if (fc_targetTypeId == ANYARRAYOID ||
		fc_targetTypeId == ANYENUMOID ||
		fc_targetTypeId == ANYRANGEOID ||
		fc_targetTypeId == ANYMULTIRANGEOID ||
		fc_targetTypeId == ANYCOMPATIBLEARRAYOID ||
		fc_targetTypeId == ANYCOMPATIBLERANGEOID ||
		fc_targetTypeId == ANYCOMPATIBLEMULTIRANGEOID)
	{
		
/*
		 * 假设可以强制转换类型验证了隐式强制转换是可以的。
		 *
		 * 这些情况不同于上述情况，因为参数的暴露类型必须是实际的数组、枚举、范围或多范围类型。特别是参数必须不是UNKNOWN常量。如果是，我们只会继续；下面，我们将调用伪类型的输入函数，这将产生一个错误。此外，如果我们拥有的是数组、枚举、范围或多范围上的域，我们必须将其重新标记为其基础类型。
		 *
		 * 注意：目前，我们实际上在这里看不到枚举上的域，因为此文件中的其他函数不会将这样的参数匹配到ANYENUM。但这最终应该会改变。
		 */
		if (fc_inputTypeId != UNKNOWNOID)
		{
			Oid			fc_baseTypeId = getBaseType(fc_inputTypeId);

			if (fc_baseTypeId != fc_inputTypeId)
			{
				RelabelType *fc_r = makeRelabelType((Expr *) fc_node,
												 fc_baseTypeId, -1,
												 InvalidOid,
												 fc_cformat);

				fc_r->location = fc_location;
				return (Node *) fc_r;
			}
			/* 不是域类型，因此原样返回 */
			return fc_node;
		}
	}
	if (fc_inputTypeId == UNKNOWNOID && IsA(fc_node, Const))
	{
		/*
		 * 输入是具有先前不确定类型的字符串常量。对其应用目标类型的typinput函数，以产生目标类型的常量。
		 *
		 * 注意：此情况不能与其他常量输入情况合并，因为typinput函数的行为不一定与类型转换函数相同。例如，int4的typinput函数将拒绝"1.2"，而浮点到整数的类型转换将四舍五入为整数。
		 *
		 * XXX 如果typinput函数不是不可变的，我们真的应该推迟函数调用的评估直到运行时。但是没有办法将typinput函数调用表示为表达式树，因为C字符串值不是Datum。（XXX 从7.3开始这是可能的，我们想这样做吗？）
		 */
		Const	   *fc_con = (Const *) fc_node;
		Const	   *fc_newcon = makeNode(Const);
		Oid			fc_baseTypeId;
		int32		fc_baseTypeMod;
		int32		fc_inputTypeMod;
		Type		fc_baseType;
		ParseCallbackState fc_pcbstate;

		/*
		 * 如果目标类型是一个域，我们想调用其基础类型的输入例程，而不是domain_in()。这样可以避免在域应用typmod时的过早失败：现有的输入例程遵循对长度检查的隐式强制转换语义，这并不总是我们想要的。所需的检查将在coerce_to_domain()内部正确应用。
		 */
		fc_baseTypeMod = fc_targetTypeMod;
		fc_baseTypeId = getBaseTypeAndTypmod(fc_targetTypeId, &fc_baseTypeMod);

		/*
		 * 对于大多数类型，我们将typmod -1传递给输入例程，因为现有的输入例程遵循对长度检查的隐式强制转换语义，这并不总是我们想要的。任何长度约束将由我们的调用者稍后应用。然而，有一个例外是INTERVAL类型，对于这个类型，我们必须传递typmod，否则它将无法遵循奇怪的SQL规范输入规则。（虽然丑陋，但这是规范的这一部分……）
		 */
		if (fc_baseTypeId == INTERVALOID)
			fc_inputTypeMod = fc_baseTypeMod;
		else
			fc_inputTypeMod = -1;

		fc_baseType = typeidType(fc_baseTypeId);

		fc_newcon->consttype = fc_baseTypeId;
		fc_newcon->consttypmod = fc_inputTypeMod;
		fc_newcon->constcollid = typeTypeCollation(fc_baseType);
		fc_newcon->constlen = typeLen(fc_baseType);
		fc_newcon->constbyval = typeByVal(fc_baseType);
		fc_newcon->constisnull = fc_con->constisnull;

		/*
		 * 我们使用原始文字的位置，而不考虑强制转换的位置。这是与9.2以前的行为的一个变化，旨在简化pg_stat_statements的使用。
		 */
		fc_newcon->location = fc_con->location;

		/*
		 * 设置指向常量文本，如果输入例程抛出错误。
		 */
		setup_parser_errposition_callback(&fc_pcbstate, fc_pstate, fc_con->location);

		/*
		 * 我们在这里假设UNKNOWN的内部表示与CSTRING相同。
		 */
		if (!fc_con->constisnull)
			fc_newcon->constvalue = stringTypeDatum(fc_baseType,
												 DatumGetCString(fc_con->constvalue),
												 fc_inputTypeMod);
		else
			fc_newcon->constvalue = stringTypeDatum(fc_baseType,
												 NULL,
												 fc_inputTypeMod);

		/*
		 * 如果是varlena值，强制它以未扩展（非烤制）格式；这避免了对外部值的任何可能依赖，并改善了表示的一致性。
		 */
		if (!fc_con->constisnull && fc_newcon->constlen == -1)
			fc_newcon->constvalue =
				PointerGetDatum(PG_DETOAST_DATUM(fc_newcon->constvalue));

#ifdef RANDOMIZE_ALLOCATED_MEMORY

		/*
		 * 对于按引用传递的数据类型，重复转换以查看输入函数是否在结果中留下任何未初始化的字节。我们只能在启用RANDOMIZE_ALLOCATED_MEMORY的情况下可靠地检测到这一点，因此在其他情况下我们不再进行测试。我们不希望输入函数中出现任何不稳定的原因是，Const节点的比较依赖于对datum的逐字节比较，因此如果输入函数留下垃圾，则本应相同的子表达式可能不会被识别为相同。请参阅pgsql-hackers 2008-04-04的讨论。
		 */
		if (!fc_con->constisnull && !fc_newcon->constbyval)
		{
			Datum		fc_val2;

			fc_val2 = stringTypeDatum(fc_baseType,
								   DatumGetCString(fc_con->constvalue),
								   fc_inputTypeMod);
			if (fc_newcon->constlen == -1)
				fc_val2 = PointerGetDatum(PG_DETOAST_DATUM(fc_val2));
			if (!datumIsEqual(fc_newcon->constvalue, fc_val2, false, fc_newcon->constlen))
				elog(WARNING, "type %s has unstable input conversion for \"%s\"",
					 typeTypeName(fc_baseType), DatumGetCString(fc_con->constvalue));
		}
#endif

		cancel_parser_errposition_callback(&fc_pcbstate);

		fc_result = (Node *) fc_newcon;

		/* 如果目标是一个域，应用约束。 */
		if (fc_baseTypeId != fc_targetTypeId)
			fc_result = coerce_to_domain(fc_result,
									  fc_baseTypeId, fc_baseTypeMod,
									  fc_targetTypeId,
									  fc_ccontext, fc_cformat, fc_location,
									  false);

		ReleaseSysCache(fc_baseType);

		return fc_result;
	}
	if (IsA(fc_node, Param) &&
		fc_pstate != NULL && fc_pstate->p_coerce_param_hook != NULL)
	{
		/*
		 * 允许CoerceParamHook决定发生的事情。它可以返回一个转换后的节点（很可能是相同的Param节点），或者返回NULL以指示我们应该继续正常的强制转换。
		 */
		fc_result = fc_pstate->p_coerce_param_hook(fc_pstate,
											 (Param *) fc_node,
											 fc_targetTypeId,
											 fc_targetTypeMod,
											 fc_location);
		if (fc_result)
			return fc_result;
	}
	if (IsA(fc_node, CollateExpr))
	{
		/*
		 * 如果我们有一个 COLLATE 子句，我们必须将强制转换推到 COLLATE 之下；或者如果目标类型
		 * 不能进行排序，则丢弃 COLLATE。这真的很丑陋，但别无选择，因为上面的对 Consts 和 Params 的
		 * 黑客行为不会发生。这个权宜之计在 coerce_to_target_type 中有后果。
		 */
		CollateExpr *fc_coll = (CollateExpr *) fc_node;

		fc_result = coerce_type(fc_pstate, (Node *) fc_coll->arg,
							 fc_inputTypeId, fc_targetTypeId, fc_targetTypeMod,
							 fc_ccontext, fc_cformat, fc_location);
		if (type_is_collatable(fc_targetTypeId))
		{
			CollateExpr *fc_newcoll = makeNode(CollateExpr);

			fc_newcoll->arg = (Expr *) fc_result;
			fc_newcoll->collOid = fc_coll->collOid;
			fc_newcoll->location = fc_coll->location;
			fc_result = (Node *) fc_newcoll;
		}
		return fc_result;
	}
	fc_pathtype = find_coercion_pathway(fc_targetTypeId, fc_inputTypeId, fc_ccontext,
									 &fc_funcId);
	if (fc_pathtype != COERCION_PATH_NONE)
	{
		if (fc_pathtype != COERCION_PATH_RELABELTYPE)
		{
			/*
			 * 生成一个表达式树，表示转换函数的运行时应用。
			 * 如果我们处理的是域目标类型，则转换函数将产生基类型，
			 * 我们需要从域的 typtypmod 中提取正确的 typmod。
			 */
			Oid			fc_baseTypeId;
			int32		fc_baseTypeMod;

			fc_baseTypeMod = fc_targetTypeMod;
			fc_baseTypeId = getBaseTypeAndTypmod(fc_targetTypeId, &fc_baseTypeMod);

			fc_result = fc_build_coercion_expression(fc_node, fc_pathtype, fc_funcId,
											   fc_baseTypeId, fc_baseTypeMod,
											   fc_ccontext, fc_cformat, fc_location);

			/*
			 * 如果是域，将其强制转换为域类型，并用域类型 ID 重新标记，
			 * 隐藏先前的强制转换节点。
			 */
			if (fc_targetTypeId != fc_baseTypeId)
				fc_result = coerce_to_domain(fc_result, fc_baseTypeId, fc_baseTypeMod,
										  fc_targetTypeId,
										  fc_ccontext, fc_cformat, fc_location,
										  true);
		}
		else
		{
			/*
			 * 我们不需要物理转换，但我们确实需要附加一个 RelabelType 节点，
			 * 以便在高级代码检查时，将表达式视为具有预期类型。
			 *
			 * 此外，域可能有超出基类型的值限制，必须考虑到这一点。
			 * 如果目标是域，则不需要 RelabelType 节点。
			 */
			fc_result = coerce_to_domain(fc_node, InvalidOid, -1, fc_targetTypeId,
									  fc_ccontext, fc_cformat, fc_location,
									  false);
			if (fc_result == fc_node)
			{
				/*
				 * XXX 我们是否可以用 exprTypmod(node) 而不是
				 * 默认的 -1 typmod 来标记结果，以节省后面的可能长度强制转换？
				 * 如果两种类型对 typmod 具有相同的解释，这将有效，虽然可能性很大但并不确定。
				 */
				RelabelType *fc_r = makeRelabelType((Expr *) fc_result,
												 fc_targetTypeId, -1,
												 InvalidOid,
												 fc_cformat);

				fc_r->location = fc_location;
				fc_result = (Node *) fc_r;
			}
		}
		return fc_result;
	}
	if (fc_inputTypeId == RECORDOID &&
		ISCOMPLEX(fc_targetTypeId))
	{
		/* 将 RECORD 强制转换为特定复杂类型 */
		return fc_coerce_record_to_complex(fc_pstate, fc_node, fc_targetTypeId,
										fc_ccontext, fc_cformat, fc_location);
	}
	if (fc_targetTypeId == RECORDOID &&
		ISCOMPLEX(fc_inputTypeId))
	{
		/* 将特定复杂类型强制转换为 RECORD */
		/* 注意：我们在这里不想要 RelabelType */
		return fc_node;
	}
#ifdef NOT_USED
	if (fc_inputTypeId == RECORDARRAYOID &&
		fc_is_complex_array(fc_targetTypeId))
	{
		/* 将 record[] 强制转换为特定复杂数组类型 */
		/* 尚未实现... */
	}
#endif
	if (fc_targetTypeId == RECORDARRAYOID &&
		fc_is_complex_array(fc_inputTypeId))
	{
		/* 将特定复杂数组类型强制转换为 record[] */
		/* 注意：我们在这里不想要 RelabelType */
		return fc_node;
	}
	if (typeInheritsFrom(fc_inputTypeId, fc_targetTypeId)
		|| fc_typeIsOfTypedTable(fc_inputTypeId, fc_targetTypeId))
	{
		/*
		 * 输入类类型是目标的子类，因此生成一个适当的运行时转换
		 * （删除不必要的列，并可能重新排列所需的列）。
		 *
		 * 当输入是目标类型的子类域时，我们也会到达这里。为了简化执行器的工作，
		 * 我们将 ConvertRowtypeExpr 定义为仅在常规复合类型之间起作用；
		 * 因此，在这种情况下插入一个 RelabelType，将输入表达式压缩到其基类型。
		 */
		Oid			fc_baseTypeId = getBaseType(fc_inputTypeId);
		ConvertRowtypeExpr *fc_r = makeNode(ConvertRowtypeExpr);

		if (fc_baseTypeId != fc_inputTypeId)
		{
			RelabelType *fc_rt = makeRelabelType((Expr *) fc_node,
											  fc_baseTypeId, -1,
											  InvalidOid,
											  COERCE_IMPLICIT_CAST);

			fc_rt->location = fc_location;
			fc_node = (Node *) fc_rt;
		}
		fc_r->arg = (Expr *) fc_node;
		fc_r->resulttype = fc_targetTypeId;
		fc_r->convertformat = fc_cformat;
		fc_r->location = fc_location;
		return (Node *) fc_r;
	}
	/* 如果我们到这里，调用者搞砸了 */
	elog(ERROR, "failed to find conversion function from %s to %s",
		 format_type_be(fc_inputTypeId), format_type_be(fc_targetTypeId));
	return NULL;				/* 保持编译器安静 */
}


/*
 * can_coerce_type()
 *		输入类型 ID 是否可以强制转换为目标类型 ID？
 *
 * 我们必须被告知上下文（CAST 结构、赋值、隐式强制转换），
 * 因为这决定了可用转换的集合。
 */
bool can_coerce_type(int fc_nargs, const Oid *fc_input_typeids, const Oid *fc_target_typeids,
				CoercionContext fc_ccontext)
{
	bool		fc_have_generics = false;
	int			fc_i;

	/* 遍历参数列表... */
	for (fc_i = 0; fc_i < fc_nargs; fc_i++)
	{
		Oid			fc_inputTypeId = fc_input_typeids[fc_i];
		Oid			fc_targetTypeId = fc_target_typeids[fc_i];
		CoercionPathType fc_pathtype;
		Oid			fc_funcId;

		/* 如果类型相同则没问题 */
		if (fc_inputTypeId == fc_targetTypeId)
			continue;

		/* 如果目标是 ANY 则接受 */
		if (fc_targetTypeId == ANYOID)
			continue;

		/* 如果目标是多态的，目前接受 */
		if (IsPolymorphicType(fc_targetTypeId))
		{
			fc_have_generics = true;	/* 以后再进行更多检查 */
			continue;
		}

		/*
		 * 如果输入是未类型化的字符串常量，则假设我们可以将其转换为
		 * 任何类型。
		 */
		if (fc_inputTypeId == UNKNOWNOID)
			continue;

		/*
		 * 如果 pg_cast 显示我们可以强制转换，则接受。
		 * 此测试现在涵盖了二进制兼容性和强制转换函数的情况。
		 */
		fc_pathtype = find_coercion_pathway(fc_targetTypeId, fc_inputTypeId, fc_ccontext,
										 &fc_funcId);
		if (fc_pathtype != COERCION_PATH_NONE)
			continue;

		/*
		 * 如果输入是 RECORD 且目标是复合类型，则假设我们可以
		 * 强制转换（可能需要更严格的检查）。
		 */
		if (fc_inputTypeId == RECORDOID &&
			ISCOMPLEX(fc_targetTypeId))
			continue;

		/*
		 * 如果输入是复合类型且目标是 RECORD，则接受。
		 */
		if (fc_targetTypeId == RECORDOID &&
			ISCOMPLEX(fc_inputTypeId))
			continue;

#ifdef NOT_USED					/* 尚未实现 */

		/*
		 * 如果输入是 record[] 且目标是复合数组类型，则假设
		 * 我们可以强制转换（可能需要更严格的检查）。
		 */
		if (fc_inputTypeId == RECORDARRAYOID &&
			fc_is_complex_array(fc_targetTypeId))
			continue;
#endif

		/*
		 * 如果输入是复合数组类型且目标是 record[]，则接受。
		 */
		if (fc_targetTypeId == RECORDARRAYOID &&
			fc_is_complex_array(fc_inputTypeId))
			continue;

		/*
		 * 如果输入是继承自目标的类类型，则接受。
		 */
		if (typeInheritsFrom(fc_inputTypeId, fc_targetTypeId)
			|| fc_typeIsOfTypedTable(fc_inputTypeId, fc_targetTypeId))
			continue;

		/*
		 * 否则，无法在此参数位置进行强制转换。
		 */
		return false;
	}

	/* 如果我们发现任何通用参数类型，请交叉检查它们 */
	if (fc_have_generics)
	{
		if (!check_generic_type_consistency(fc_input_typeids, fc_target_typeids,
											fc_nargs))
			return false;
	}

	return true;
}


/*
 * 创建一个表达式树以表示强制转换为域类型。
 *
 * 'arg'：输入表达式
 * 'baseTypeId'：域的基类型，若已知（如果调用者
 *		未查看此信息，则传递 InvalidOid）
 * 'baseTypeMod'：域的基类型 typmod，若已知（如果调用者
 *		未查看此信息，则传递 -1）
 * 'typeId'：要强制转换为的目标类型
 * 'ccontext'：控制强制转换的上下文指示符
 * 'cformat'：强制转换显示格式
 * 'location'：强制转换请求位置
 * 'hideInputCoercion'：如果为真，则在此强制转换下隐藏输入强制转换。
 *
 * 如果目标类型不是域，则按原样返回给定的 'arg'。
 */
Node * coerce_to_domain(Node *fc_arg, Oid fc_baseTypeId, int32 fc_baseTypeMod, Oid fc_typeId,
				 CoercionContext fc_ccontext, CoercionForm fc_cformat, int fc_location,
				 bool fc_hideInputCoercion)
{
	CoerceToDomain *fc_result;

	/* 获取基本类型，如果尚未提供 */
	if (fc_baseTypeId == InvalidOid)
		fc_baseTypeId = getBaseTypeAndTypmod(fc_typeId, &fc_baseTypeMod);

	/* 如果不是域，按原样返回节点 */
	if (fc_baseTypeId == fc_typeId)
		return fc_arg;

	/* 抑制嵌套强制步骤的显示 */
	if (fc_hideInputCoercion)
		fc_hide_coercion_node(fc_arg);

	/*
	 * 如果域对其基本类型应用了 typmod，构建适当的强制步骤。
	 * 为了显示目的，将其标记为隐式，因为我们不想让它由 ruleutils.c 单独显示；
	 * 但是传递给转换函数的 isExplicit 标志取决于
	 * 域强制如何调用，因此隐式和显式强制的语义不同。
	 * （这真的是我们想要的行为吗？）
	 *
	 * 注意：由于我们将其作为固定表达式结构的一部分应用，
	 * ALTER DOMAIN 不能更改 typtypmod。但不清楚这样做是否安全，
	 * 因为没有很多关于基本类型对 typmod 的含义的了解。
	 */
	fc_arg = fc_coerce_type_typmod(fc_arg, fc_baseTypeId, fc_baseTypeMod,
							 fc_ccontext, COERCE_IMPLICIT_CAST, fc_location,
							 false);

	/*
	 * 现在构建域强制节点。这表示对当前附加到域的任何约束的运行时检查。
	 * 这还确保表达式的结果类型被正确标记。
	 */
	fc_result = makeNode(CoerceToDomain);
	fc_result->arg = (Expr *) fc_arg;
	fc_result->resulttype = fc_typeId;
	fc_result->resulttypmod = -1;	/* 目前，对于域总是 -1 */
	/* resultcollid 将由 parse_collate.c 设置 */
	fc_result->coercionformat = fc_cformat;
	fc_result->location = fc_location;

	return (Node *) fc_result;
}


/*
 * coerce_type_typmod()
 *		强制一个值为特定的 typmod，如果有意义且可能。
 *
 * 这适用于将要存储在关系中的值
 * （在此我们为列有一个 atttypmod）以及被显式 CAST 的值
 * （其中 typmod 来自目标类型规范）。
 *
 * 调用者必须已经确保值是正确的类型，
 * 通常通过应用 coerce_type。
 *
 * ccontext 可能会影响语义，具体取决于长度强制
 * 函数是否注意到它传递的 isExplicit 标志。
 *
 * cformat 决定生成节点（如果有）的显示属性。
 *
 * 如果 hideInputCoercion 为 true *且*我们生成节点，则输入节点被
 * 强制为隐式显示形式，以便在显示表达式时仅显示 typmod 强制节点。
 *
 * 注意：这不需要在域类型上工作，因为任何域的 typmod
 * 强制被认为是产生域值所需的类型强制的一部分。
 * 所以，不需要 getBaseType。
 */
static Node * fc_coerce_type_typmod(Node *fc_node, Oid fc_targetTypeId, int32 fc_targetTypMod,
				   CoercionContext fc_ccontext, CoercionForm fc_cformat,
				   int fc_location,
				   bool fc_hideInputCoercion)
{
	CoercionPathType fc_pathtype;
	Oid			fc_funcId;

	/* 如果已完成则跳过强制 */
	if (fc_targetTypMod == exprTypmod(fc_node))
		return fc_node;

	/* 抑制嵌套强制步骤的显示 */
	if (fc_hideInputCoercion)
		fc_hide_coercion_node(fc_node);

	/*
	 * 负的 typmod 意味着不需要实际强制，但是我们仍然
	 * 希望有一个 RelabelType 以确保表达式暴露预期的
	 * typmod。
	 */
	if (fc_targetTypMod < 0)
		fc_pathtype = COERCION_PATH_NONE;
	else
		fc_pathtype = find_typmod_coercion_function(fc_targetTypeId, &fc_funcId);

	if (fc_pathtype != COERCION_PATH_NONE)
	{
		fc_node = fc_build_coercion_expression(fc_node, fc_pathtype, fc_funcId,
										 fc_targetTypeId, fc_targetTypMod,
										 fc_ccontext, fc_cformat, fc_location);
	}
	else
	{
		/*
		 * 我们不需要执行任何实际的强制步骤，但我们应该
		 * 应用一个 RelabelType，以确保表达式暴露
		 * 预期的 typmod。
		 */
		fc_node = applyRelabelType(fc_node, fc_targetTypeId, fc_targetTypMod,
								exprCollation(fc_node),
								fc_cformat, fc_location, false);
	}

	return fc_node;
}

/*
 * 将强制节点标记为隐式，以便它永远不会被
 * ruleutils.c 显示。我们在生成一组强制节点时使用
 * 这一点，以实现逻辑上一个转换；内部节点被
 * 强制为隐式 CAST 格式。这不会改变它们的语义，
 * 仅改变显示行为。
 *
 * 在没有 CoercionForm 字段的情况下调用此函数是调用者的错误。
 */
static void fc_hide_coercion_node(Node *fc_node)
{
	if (IsA(fc_node, FuncExpr))
		((FuncExpr *) fc_node)->funcformat = COERCE_IMPLICIT_CAST;
	else if (IsA(fc_node, RelabelType))
		((RelabelType *) fc_node)->relabelformat = COERCE_IMPLICIT_CAST;
	else if (IsA(fc_node, CoerceViaIO))
		((CoerceViaIO *) fc_node)->coerceformat = COERCE_IMPLICIT_CAST;
	else if (IsA(fc_node, ArrayCoerceExpr))
		((ArrayCoerceExpr *) fc_node)->coerceformat = COERCE_IMPLICIT_CAST;
	else if (IsA(fc_node, ConvertRowtypeExpr))
		((ConvertRowtypeExpr *) fc_node)->convertformat = COERCE_IMPLICIT_CAST;
	else if (IsA(fc_node, RowExpr))
		((RowExpr *) fc_node)->row_format = COERCE_IMPLICIT_CAST;
	else if (IsA(fc_node, CoerceToDomain))
		((CoerceToDomain *) fc_node)->coercionformat = COERCE_IMPLICIT_CAST;
	else
		elog(ERROR, "unsupported node type: %d", (int) nodeTag(fc_node));
}

/*
 * build_coercion_expression()
 *		构建一个表达式树以应用 pg_cast 条目。
 *
 * 这同时用于类型强制和长度强制操作，
 * 因为在调用约定上没有区别。
 */
static Node * fc_build_coercion_expression(Node *fc_node,
						  CoercionPathType fc_pathtype,
						  Oid fc_funcId,
						  Oid fc_targetTypeId, int32 fc_targetTypMod,
						  CoercionContext fc_ccontext, CoercionForm fc_cformat,
						  int fc_location)
{
	int			fc_nargs = 0;

	if (OidIsValid(fc_funcId))
	{
		HeapTuple	fc_tp;
		Form_pg_proc fc_procstruct;

		fc_tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(fc_funcId));
		if (!HeapTupleIsValid(fc_tp))
			elog(ERROR, "cache lookup failed for function %u", fc_funcId);
		fc_procstruct = (Form_pg_proc) GETSTRUCT(fc_tp);

		/*
		 * 这些断言基本上检查函数是否合法的强制函数。
		 * 我们无法在 prorettype 和 proargtypes[0] 上进行
		 * 表面上显而易见的测试，即使在 COERCION_PATH_FUNC 的情况下，
		 * 也因为各种二进制兼容性问题。
		 */
		/* 断言(targetTypeId == procstruct->prorettype); */
		Assert(!fc_procstruct->proretset);
		Assert(fc_procstruct->prokind == PROKIND_FUNCTION);
		fc_nargs = fc_procstruct->pronargs;
		Assert(fc_nargs >= 1 && fc_nargs <= 3);
		/* 断言(procstruct->proargtypes.values[0] == exprType(node)); */
		Assert(fc_nargs < 2 || fc_procstruct->proargtypes.values[1] == INT4OID);
		Assert(fc_nargs < 3 || fc_procstruct->proargtypes.values[2] == BOOLOID);

		ReleaseSysCache(fc_tp);
	}

	if (fc_pathtype == COERCION_PATH_FUNC)
	{
		/* 我们构建一个普通的 FuncExpr，并带有特殊参数 */
		FuncExpr   *fc_fexpr;
		List	   *fc_args;
		Const	   *fc_cons;

		Assert(OidIsValid(fc_funcId));

		fc_args = list_make1(fc_node);

		if (fc_nargs >= 2)
		{
			/* 将目标 typmod 作为 int4 常量传递 */
			fc_cons = makeConst(INT4OID,
							 -1,
							 InvalidOid,
							 sizeof(int32),
							 Int32GetDatum(fc_targetTypMod),
							 false,
							 true);

			fc_args = lappend(fc_args, fc_cons);
		}

		if (fc_nargs == 3)
		{
			/* 也传递一个布尔的 isExplicit 参数 */
			fc_cons = makeConst(BOOLOID,
							 -1,
							 InvalidOid,
							 sizeof(bool),
							 BoolGetDatum(fc_ccontext == COERCION_EXPLICIT),
							 false,
							 true);

			fc_args = lappend(fc_args, fc_cons);
		}

		fc_fexpr = makeFuncExpr(fc_funcId, fc_targetTypeId, fc_args,
							 InvalidOid, InvalidOid, fc_cformat);
		fc_fexpr->location = fc_location;
		return (Node *) fc_fexpr;
	}
	else if (fc_pathtype == COERCION_PATH_ARRAYCOERCE)
	{
		/* 我们需要构建一个 ArrayCoerceExpr */
		ArrayCoerceExpr *fc_acoerce = makeNode(ArrayCoerceExpr);
		CaseTestExpr *fc_ctest = makeNode(CaseTestExpr);
		Oid			fc_sourceBaseTypeId;
		int32		fc_sourceBaseTypeMod;
		Oid			fc_targetElementType;
		Node	   *fc_elemexpr;

		/*
		 * 查看源数组类型上的任何域。注意我们不
		 * 期望目标类型是一个域；它必须是一个普通数组。
		 * （要达到域目标类型，我们稍后会进行 coerce_to_domain。）
		 */
		fc_sourceBaseTypeMod = exprTypmod(fc_node);
		fc_sourceBaseTypeId = getBaseTypeAndTypmod(exprType(fc_node),
												&fc_sourceBaseTypeMod);

		/*
		 * 设置一个 CaseTestExpr，表示源数组的一个元素。
		 * 这是对 CaseTestExpr 的滥用，但只要在完成的
		 * elemexpr 中不能有任何 CaseExpr 或 ArrayCoerceExpr，则可以。
		 */
		fc_ctest->typeId = get_element_type(fc_sourceBaseTypeId);
		Assert(OidIsValid(fc_ctest->typeId));
		fc_ctest->typeMod = fc_sourceBaseTypeMod;
		fc_ctest->collation = InvalidOid;	/* 假设强制不在乎 */

		/* 并将其强制转换为目标元素类型 */
		fc_targetElementType = get_element_type(fc_targetTypeId);
		Assert(OidIsValid(fc_targetElementType));

		fc_elemexpr = coerce_to_target_type(NULL,
										 (Node *) fc_ctest,
										 fc_ctest->typeId,
										 fc_targetElementType,
										 fc_targetTypMod,
										 fc_ccontext,
										 fc_cformat,
										 fc_location);
		if (fc_elemexpr == NULL)	/* 不应该发生 */
			elog(ERROR, "failed to coerce array element type as expected");

		fc_acoerce->arg = (Expr *) fc_node;
		fc_acoerce->elemexpr = (Expr *) fc_elemexpr;
		fc_acoerce->resulttype = fc_targetTypeId;

		/*
		 * 仅在我们最终得到的每个元素表达式被标记为这种方式时
		 * 才将输出标记为具有特定的元素 typmod。
		 */
		fc_acoerce->resulttypmod = exprTypmod(fc_elemexpr);
		/* resultcollid 将由 parse_collate.c 设置 */
		fc_acoerce->coerceformat = fc_cformat;
		fc_acoerce->location = fc_location;

		return (Node *) fc_acoerce;
	}
	else if (fc_pathtype == COERCION_PATH_COERCEVIAIO)
	{
		/* 我们需要构建一个 CoerceViaIO 节点 */
		CoerceViaIO *fc_iocoerce = makeNode(CoerceViaIO);

		Assert(!OidIsValid(fc_funcId));

		fc_iocoerce->arg = (Expr *) fc_node;
		fc_iocoerce->resulttype = fc_targetTypeId;
		/* resultcollid 将由 parse_collate.c 设置 */
		fc_iocoerce->coerceformat = fc_cformat;
		fc_iocoerce->location = fc_location;

		return (Node *) fc_iocoerce;
	}
	else
	{
		elog(ERROR, "unsupported pathtype %d in build_coercion_expression",
			 (int) fc_pathtype);
		return NULL;			/* 保持编译器安静 */
	}
}


/*
 * coerce_record_to_complex
 *		将 RECORD 强制转换为特定的复合类型。
 *
 * 目前我们仅支持输入为 RowExprs 或整行 Vars 的情况。
 */
static Node * fc_coerce_record_to_complex(ParseState *fc_pstate, Node *fc_node,
						 Oid fc_targetTypeId,
						 CoercionContext fc_ccontext,
						 CoercionForm fc_cformat,
						 int fc_location)
{
	RowExpr    *fc_rowexpr;
	Oid			fc_baseTypeId;
	int32		fc_baseTypeMod = -1;
	TupleDesc	fc_tupdesc;
	List	   *fc_args = NIL;
	List	   *fc_newargs;
	int			fc_i;
	int			fc_ucolno;
	ListCell   *fc_arg;

	if (fc_node && IsA(fc_node, RowExpr))
	{
		/*
		 * 由于 RowExpr 必须为 RECORD 类型，因此我们不必担心它
		 * 包含任何删除的列。
		 */
		fc_args = ((RowExpr *) fc_node)->args;
	}
	else if (fc_node && IsA(fc_node, Var) &&
			 ((Var *) fc_node)->varattno == InvalidAttrNumber)
	{
		int			fc_rtindex = ((Var *) fc_node)->varno;
		int			fc_sublevels_up = ((Var *) fc_node)->varlevelsup;
		int			fc_vlocation = ((Var *) fc_node)->location;
		ParseNamespaceItem *fc_nsitem;

		fc_nsitem = GetNSItemByRangeTablePosn(fc_pstate, fc_rtindex, fc_sublevels_up);
		fc_args = expandNSItemVars(fc_nsitem, fc_sublevels_up, fc_vlocation, NULL);
	}
	else
		ereport(ERROR,
				(errcode(ERRCODE_CANNOT_COERCE),
				 errmsg("cannot cast type %s to %s",
						format_type_be(RECORDOID),
						format_type_be(fc_targetTypeId)),
				 parser_coercion_errposition(fc_pstate, fc_location, fc_node)));

	/*
	 * 查找复合类型，考虑到我们所给的可能是复合类型的域。
	 */
	fc_baseTypeId = getBaseTypeAndTypmod(fc_targetTypeId, &fc_baseTypeMod);
	fc_tupdesc = lookup_rowtype_tupdesc(fc_baseTypeId, fc_baseTypeMod);

	/* 处理字段 */
	fc_newargs = NIL;
	fc_ucolno = 1;
	fc_arg = list_head(fc_args);
	for (fc_i = 0; fc_i < fc_tupdesc->natts; fc_i++)
	{
		Node	   *fc_expr;
		Node	   *fc_cexpr;
		Oid			fc_exprtype;
		Form_pg_attribute fc_attr = TupleDescAttr(fc_tupdesc, fc_i);

		/* 填充行类型中丢弃列的 NULL 值 */
		if (fc_attr->attisdropped)
		{
			/* 
			 * 这里不能使用 atttypid，但 Const 声称是什么类型并不重要。
			 */
			fc_newargs = lappend(fc_newargs,
							  makeNullConst(INT4OID, -1, InvalidOid));
			continue;
		}

		if (fc_arg == NULL)
			ereport(ERROR,
					(errcode(ERRCODE_CANNOT_COERCE),
					 errmsg("cannot cast type %s to %s",
							format_type_be(RECORDOID),
							format_type_be(fc_targetTypeId)),
					 errdetail("Input has too few columns."),
					 parser_coercion_errposition(fc_pstate, fc_location, fc_node)));
		fc_expr = (Node *) lfirst(fc_arg);
		fc_exprtype = exprType(fc_expr);

		fc_cexpr = coerce_to_target_type(fc_pstate,
									  fc_expr, fc_exprtype,
									  fc_attr->atttypid,
									  fc_attr->atttypmod,
									  fc_ccontext,
									  COERCE_IMPLICIT_CAST,
									  -1);
		if (fc_cexpr == NULL)
			ereport(ERROR,
					(errcode(ERRCODE_CANNOT_COERCE),
					 errmsg("cannot cast type %s to %s",
							format_type_be(RECORDOID),
							format_type_be(fc_targetTypeId)),
					 errdetail("Cannot cast type %s to %s in column %d.",
							   format_type_be(fc_exprtype),
							   format_type_be(fc_attr->atttypid),
							   fc_ucolno),
					 parser_coercion_errposition(fc_pstate, fc_location, fc_expr)));
		fc_newargs = lappend(fc_newargs, fc_cexpr);
		fc_ucolno++;
		fc_arg = lnext(fc_args, fc_arg);
	}
	if (fc_arg != NULL)
		ereport(ERROR,
				(errcode(ERRCODE_CANNOT_COERCE),
				 errmsg("cannot cast type %s to %s",
						format_type_be(RECORDOID),
						format_type_be(fc_targetTypeId)),
				 errdetail("Input has too many columns."),
				 parser_coercion_errposition(fc_pstate, fc_location, fc_node)));

	ReleaseTupleDesc(fc_tupdesc);

	fc_rowexpr = makeNode(RowExpr);
	fc_rowexpr->args = fc_newargs;
	fc_rowexpr->row_typeid = fc_baseTypeId;
	fc_rowexpr->row_format = fc_cformat;
	fc_rowexpr->colnames = NIL;	/* 对于命名目标类型不需要 */
	fc_rowexpr->location = fc_location;

	/* 如果目标是一个域，则应用约束 */
	if (fc_baseTypeId != fc_targetTypeId)
	{
		fc_rowexpr->row_format = COERCE_IMPLICIT_CAST;
		return coerce_to_domain((Node *) fc_rowexpr,
								fc_baseTypeId, fc_baseTypeMod,
								fc_targetTypeId,
								fc_ccontext, fc_cformat, fc_location,
								false);
	}

	return (Node *) fc_rowexpr;
}

/*
 * coerce_to_boolean()
 *		强制构造的参数为布尔输入(AND, OR, NOT 等)。 还检查输入不是一个集合。
 *
 * 返回可能转化的节点树。
 *
 * 与 coerce_type 一样，如果不想要特殊的未知参数处理，pstate 可能为 NULL。
 */
Node * coerce_to_boolean(ParseState *fc_pstate, Node *fc_node,
				  const char *fc_constructName)
{
	Oid			fc_inputTypeId = exprType(fc_node);

	if (fc_inputTypeId != BOOLOID)
	{
		Node	   *fc_newnode;

		fc_newnode = coerce_to_target_type(fc_pstate, fc_node, fc_inputTypeId,
										BOOLOID, -1,
										COERCION_ASSIGNMENT,
										COERCE_IMPLICIT_CAST,
										-1);
		if (fc_newnode == NULL)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
			/* 翻译者：第一个 %s 是 SQL 构造的名称，例如 WHERE */
					 errmsg("argument of %s must be type %s, not type %s",
							fc_constructName, "boolean",
							format_type_be(fc_inputTypeId)),
					 parser_errposition(fc_pstate, exprLocation(fc_node))));
		fc_node = fc_newnode;
	}

	if (expression_returns_set(fc_node))
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
		/* 翻译者：%s 是 SQL 构造的名称，例如 WHERE */
				 errmsg("argument of %s must not return a set",
						fc_constructName),
				 parser_errposition(fc_pstate, exprLocation(fc_node))));

	return fc_node;
}

/*
 * coerce_to_specific_type_typmod()
 *		强制构造的参数为特定数据类型，具有特定的 typmod。 还检查输入不是一个集合。
 *
 * 返回可能转化的节点树。
 *
 * 与 coerce_type 一样，如果不想要特殊的未知参数处理，pstate 可能为 NULL。
 */
Node * coerce_to_specific_type_typmod(ParseState *fc_pstate, Node *fc_node,
							   Oid fc_targetTypeId, int32 fc_targetTypmod,
							   const char *fc_constructName)
{
	Oid			fc_inputTypeId = exprType(fc_node);

	if (fc_inputTypeId != fc_targetTypeId)
	{
		Node	   *fc_newnode;

		fc_newnode = coerce_to_target_type(fc_pstate, fc_node, fc_inputTypeId,
										fc_targetTypeId, fc_targetTypmod,
										COERCION_ASSIGNMENT,
										COERCE_IMPLICIT_CAST,
										-1);
		if (fc_newnode == NULL)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
			/* 翻译者：第一个 %s 是 SQL 构造的名称，例如 LIMIT */
					 errmsg("argument of %s must be type %s, not type %s",
							fc_constructName,
							format_type_be(fc_targetTypeId),
							format_type_be(fc_inputTypeId)),
					 parser_errposition(fc_pstate, exprLocation(fc_node))));
		fc_node = fc_newnode;
	}

	if (expression_returns_set(fc_node))
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
		/* 翻译者：%s 是 SQL 构造的名称，例如 LIMIT */
				 errmsg("argument of %s must not return a set",
						fc_constructName),
				 parser_errposition(fc_pstate, exprLocation(fc_node))));

	return fc_node;
}

/*
 * coerce_to_specific_type()
 *		强制构造的参数为特定数据类型。 还检查输入不是一个集合。
 *
 * 返回可能转化的节点树。
 *
 * 与 coerce_type 一样，如果不想要特殊的未知参数处理，pstate 可能为 NULL。
 */
Node * coerce_to_specific_type(ParseState *fc_pstate, Node *fc_node,
						Oid fc_targetTypeId,
						const char *fc_constructName)
{
	return coerce_to_specific_type_typmod(fc_pstate, fc_node,
										  fc_targetTypeId, -1,
										  fc_constructName);
}

/*
 * coerce_null_to_domain()
 *		构建一个 NULL 常量，然后在 CoerceToDomain 中包装它
 *		如果所需类型是一个域类型。这允许在运行时强制执行任何
 *		NOT NULL 域约束。
 */
Node * coerce_null_to_domain(Oid fc_typid, int32 fc_typmod, Oid fc_collation,
					  int fc_typlen, bool fc_typbyval)
{
	Node	   *fc_result;
	Oid			fc_baseTypeId;
	int32		fc_baseTypeMod = fc_typmod;

	/*
	 * 常量必须看起来具有域的基础类型/typmod，否则
	 * coerce_to_domain() 将应用无用的长度强制转换。
	 */
	fc_baseTypeId = getBaseTypeAndTypmod(fc_typid, &fc_baseTypeMod);
	fc_result = (Node *) makeConst(fc_baseTypeId,
								fc_baseTypeMod,
								fc_collation,
								fc_typlen,
								(Datum) 0,
								true,	/* isnull */
								fc_typbyval);
	if (fc_typid != fc_baseTypeId)
		fc_result = coerce_to_domain(fc_result,
								  fc_baseTypeId, fc_baseTypeMod,
								  fc_typid,
								  COERCION_IMPLICIT,
								  COERCE_IMPLICIT_CAST,
								  -1,
								  false);
	return fc_result;
}

/*
 * parser_coercion_errposition - 如果可能，报告强制错误的位置
 *
 * 我们更倾向于指向强制请求 (CAST, :: 等)；但是在隐式强制的情况下可能没有这样的位置信息。
 * 在这种情况下，指向输入表达式。
 *
 * XXX 可能这比强制错误更普遍有用；
 * 如果是这样，应该重命名并与 parser_errposition 一起放置。
 */
int parser_coercion_errposition(ParseState *fc_pstate,
							int fc_coerce_location,
							Node *fc_input_expr)
{
	if (fc_coerce_location >= 0)
		return parser_errposition(fc_pstate, fc_coerce_location);
	else
		return parser_errposition(fc_pstate, exprLocation(fc_input_expr));
}


/*
 * select_common_type()
 *		确定输入表达式列表的共同超类型。
 *		这用于确定 CASE、UNION 和类似构造的输出类型。
 *
 * 'exprs' 是一个 *非空* 表达式列表。 注意，列表中较早的项
 * 如果有怀疑，将被优先考虑。
 * 'context' 是在我们无法选择可用类型时用于错误消息的短语。 传入 NULL 让例程返回 InvalidOid
 * 而不是在失败时引发错误。
 * 'which_expr'：如果不为 NULL，接收一个指向特定输入的指针
 * 表达式，以便从中获取结果类型。
 *
 * 警告：“失败”仅意味着输入的类型类别不同。
 * 并不能保证所有输入都可以强制转换为
 * 选定的类型；调用者必须检查 (参见 verify_common_type)。
 */
Oid select_common_type(ParseState *fc_pstate, List *fc_exprs, const char *fc_context,
				   Node **fc_which_expr)
{
	Node	   *fc_pexpr;
	Oid			fc_ptype;
	TYPCATEGORY fc_pcategory;
	bool		fc_pispreferred;
	ListCell   *fc_lc;

	Assert(fc_exprs != NIL);
	fc_pexpr = (Node *) linitial(fc_exprs);
	fc_lc = list_second_cell(fc_exprs);
	fc_ptype = exprType(fc_pexpr);

	/*
	 * 如果所有输入类型有效且完全相同，则只选择该类型。
	 * 这是我们将结果视为域类型的唯一方法；
	 * 否则，域被压缩为其基础类型进行比较。
	 */
	if (fc_ptype != UNKNOWNOID)
	{
		for_each_cell(fc_lc, fc_exprs, fc_lc)
		{
			Node	   *fc_nexpr = (Node *) lfirst(fc_lc);
			Oid			fc_ntype = exprType(fc_nexpr);

			if (fc_ntype != fc_ptype)
				break;
		}
		if (fc_lc == NULL)			/* 到达列表的末尾了吗？ */
		{
			if (fc_which_expr)
				*fc_which_expr = fc_pexpr;
			return fc_ptype;
		}
	}

	/*
	 * 不，所以设置完整算法。 注意此时，lc
	 * 指向与 pexpr 类型不同的第一个列表项；我们无需重新检查
	 * 前一个循环已推进的任何项。
	 */
	fc_ptype = getBaseType(fc_ptype);
	get_type_category_preferred(fc_ptype, &fc_pcategory, &fc_pispreferred);

	for_each_cell(fc_lc, fc_exprs, fc_lc)
	{
		Node	   *fc_nexpr = (Node *) lfirst(fc_lc);
		Oid			fc_ntype = getBaseType(exprType(fc_nexpr));

		/* 如果没有新的信息则继续下一个... */
		if (fc_ntype != UNKNOWNOID && fc_ntype != fc_ptype)
		{
			TYPCATEGORY fc_ncategory;
			bool		fc_nispreferred;

			get_type_category_preferred(fc_ntype, &fc_ncategory, &fc_nispreferred);
			if (fc_ptype == UNKNOWNOID)
			{
				/* 到目前为止，只有未知值，所以接受任何... */
				fc_pexpr = fc_nexpr;
				fc_ptype = fc_ntype;
				fc_pcategory = fc_ncategory;
				fc_pispreferred = fc_nispreferred;
			}
			else if (fc_ncategory != fc_pcategory)
			{
				/*
				 * 两种类型在不同类别中？ 那就没什么希望了...
				 */
				if (fc_context == NULL)
					return InvalidOid;
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
				/*------
				  翻译者：第一个 %s 是 SQL 构造的名称，例如 CASE */
						 errmsg("%s types %s and %s cannot be matched",
								fc_context,
								format_type_be(fc_ptype),
								format_type_be(fc_ntype)),
						 parser_errposition(fc_pstate, exprLocation(fc_nexpr))));
			}
			else if (!fc_pispreferred &&
					 can_coerce_type(1, &fc_ptype, &fc_ntype, COERCION_IMPLICIT) &&
					 !can_coerce_type(1, &fc_ntype, &fc_ptype, COERCION_IMPLICIT))
			{
				/*
				 * 如果可以隐式强制转换为新类型，但反之则不可，则取新类型；
				 * 但如果我们有首选类型，则保持在其上。
				 */
				fc_pexpr = fc_nexpr;
				fc_ptype = fc_ntype;
				fc_pcategory = fc_ncategory;
				fc_pispreferred = fc_nispreferred;
			}
		}
	}

	/*
	 * 如果所有输入都是 UNKNOWN 类型 --- 即，未知类型文字 ---
	 * 则解析为 TEXT 类型。 此情况出现在像 SELECT (CASE WHEN foo THEN 'bar' ELSE 'baz' END); SELECT 'foo'
	 * UNION SELECT 'bar'; 这样的构造中。 看似希望将构造的输出类型留为 UNKNOWN，但这实际上行不通，
	 * 因为我们可能在运行时需要从 UNKNOWN 强制转换到其他类型，通常我们不会有。
	 * 我们需要在未知文字仍然是文字时强制其转换，因此必须立即做出决定。
	 */
	if (fc_ptype == UNKNOWNOID)
		fc_ptype = TEXTOID;

	if (fc_which_expr)
		*fc_which_expr = fc_pexpr;
	return fc_ptype;
}


/*
 * select_common_type_from_oids()
 *		确定一组类型 OID 的公共超类型。
 *
 * 这与 select_common_type() 的逻辑相同，但从一个类型 OID 数组中工作，而不是表达式列表。与该函数一样，数组中的早期条目对后来的条目有一些优先权。
 * 失败时，如果 noerror 为真则返回 InvalidOid，否则抛出错误。
 *
 * 注意：“失败”只是意味着存在不同类型类别的输入。并不能保证所有输入都可以转换为选定类型；调用者必须检查这一点（见 verify_common_type_from_oids）。
 *
 * 注意：调用者不会传递任何 UNKNOWNOID 条目，因此该函数中的测试是死代码。 但是，它们的成本不高，似乎更好地将此逻辑保持尽可能接近 select_common_type()。
 */
static Oid fc_select_common_type_from_oids(int fc_nargs, const Oid *fc_typeids, bool fc_noerror)
{
	Oid			fc_ptype;
	TYPCATEGORY fc_pcategory;
	bool		fc_pispreferred;
	int			fc_i = 1;

	Assert(fc_nargs > 0);
	fc_ptype = fc_typeids[0];

	/* 如果所有输入类型都是有效且完全相同，则选择该类型。 */
	if (fc_ptype != UNKNOWNOID)
	{
		for (; fc_i < fc_nargs; fc_i++)
		{
			if (fc_typeids[fc_i] != fc_ptype)
				break;
		}
		if (fc_i == fc_nargs)
			return fc_ptype;
	}

	/*
	 * 不行，因此为完整算法设置准备。注意，此时，我们
	 * 可以跳过“i”之前的数组条目；它们都等于 ptype。
	 */
	fc_ptype = getBaseType(fc_ptype);
	get_type_category_preferred(fc_ptype, &fc_pcategory, &fc_pispreferred);

	for (; fc_i < fc_nargs; fc_i++)
	{
		Oid			fc_ntype = getBaseType(fc_typeids[fc_i]);

		/* 如果没有新的信息则继续下一个... */
		if (fc_ntype != UNKNOWNOID && fc_ntype != fc_ptype)
		{
			TYPCATEGORY fc_ncategory;
			bool		fc_nispreferred;

			get_type_category_preferred(fc_ntype, &fc_ncategory, &fc_nispreferred);
			if (fc_ptype == UNKNOWNOID)
			{
				/* 到目前为止，只有未知值，所以接受任何... */
				fc_ptype = fc_ntype;
				fc_pcategory = fc_ncategory;
				fc_pispreferred = fc_nispreferred;
			}
			else if (fc_ncategory != fc_pcategory)
			{
				/*
				 * 两种类型在不同类别中？ 那就没什么希望了...
				 */
				if (fc_noerror)
					return InvalidOid;
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("argument types %s and %s cannot be matched",
								format_type_be(fc_ptype),
								format_type_be(fc_ntype))));
			}
			else if (!fc_pispreferred &&
					 can_coerce_type(1, &fc_ptype, &fc_ntype, COERCION_IMPLICIT) &&
					 !can_coerce_type(1, &fc_ntype, &fc_ptype, COERCION_IMPLICIT))
			{
				/*
				 * 如果可以隐式强制转换为新类型，但反之则不可，则取新类型；
				 * 但如果我们有首选类型，则保持在其上。
				 */
				fc_ptype = fc_ntype;
				fc_pcategory = fc_ncategory;
				fc_pispreferred = fc_nispreferred;
			}
		}
	}

	/* 像 select_common_type() 一样，如果所有输入都是 UNKNOWN，则选择 TEXT */
	if (fc_ptype == UNKNOWNOID)
		fc_ptype = TEXTOID;

	return fc_ptype;
}

/*
 * coerce_to_common_type()
 *		强制表达式转换为给定类型。
 *
 * 这在 select_common_type() 之后使用，以将单个
 * 表达式强制转换为所需类型。'context' 是在强制转换失败时，在错误消息中使用的短语。
 *
 * 与 coerce_type 一样，如果不需要特殊的 unknown-Param
 * 处理，则 pstate 可能为 NULL。
 */
Node * coerce_to_common_type(ParseState *fc_pstate, Node *fc_node,
					  Oid fc_targetTypeId, const char *fc_context)
{
	Oid			fc_inputTypeId = exprType(fc_node);

	if (fc_inputTypeId == fc_targetTypeId)
		return fc_node;			/* 无工作 */
	if (can_coerce_type(1, &fc_inputTypeId, &fc_targetTypeId, COERCION_IMPLICIT))
		fc_node = coerce_type(fc_pstate, fc_node, fc_inputTypeId, fc_targetTypeId, -1,
						   COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1);
	else
		ereport(ERROR,
				(errcode(ERRCODE_CANNOT_COERCE),
		/* 翻译者：第一个 %s 是 SQL 构造的名称，例如 CASE */
				 errmsg("%s could not convert type %s to %s",
						fc_context,
						format_type_be(fc_inputTypeId),
						format_type_be(fc_targetTypeId)),
				 parser_errposition(fc_pstate, exprLocation(fc_node))));
	return fc_node;
}

/*
 * verify_common_type()
 *		验证所有输入类型是否可以被强制转换为提议的公共类型。
 *		如果可以则返回 true，若不是所有的强制转换都是可能的则返回 false。
 *
 * 大多数调用 select_common_type() 的函数不需要显式地进行这一步
 * 因为在尝试将输入表达式转换为正确类型时，检查会自动进行，
 * 例如，在 coerce_to_common_type() 中。然而，如果需要单独
 * 的检查步骤以验证公共类型的适用性，请调用此函数。
 */
bool verify_common_type(Oid fc_common_type, List *fc_exprs)
{
	ListCell   *fc_lc;

	foreach(fc_lc, fc_exprs)
	{
		Node	   *fc_nexpr = (Node *) lfirst(fc_lc);
		Oid			fc_ntype = exprType(fc_nexpr);

		if (!can_coerce_type(1, &fc_ntype, &fc_common_type, COERCION_IMPLICIT))
			return false;
	}
	return true;
}

/*
 * verify_common_type_from_oids()
 *		如上，但从一个类型 OID 数组中工作。
 */
static bool fc_verify_common_type_from_oids(Oid fc_common_type, int fc_nargs, const Oid *fc_typeids)
{
	for (int fc_i = 0; fc_i < fc_nargs; fc_i++)
	{
		if (!can_coerce_type(1, &fc_typeids[fc_i], &fc_common_type, COERCION_IMPLICIT))
			return false;
	}
	return true;
}

/*
 * select_common_typmod()
 *		确定输入表达式列表的公共 typmod。
 *
 * common_type 是表达式的选定公共类型，通常
 * 使用 select_common_type() 计算得出。
 */
int32 select_common_typmod(ParseState *fc_pstate, List *fc_exprs, Oid fc_common_type)
{
	ListCell   *fc_lc;
	bool		fc_first = true;
	int32		fc_result = -1;

	foreach(fc_lc, fc_exprs)
	{
		Node	   *fc_expr = (Node *) lfirst(fc_lc);

		/* 类型必须匹配 */
		if (exprType(fc_expr) != fc_common_type)
			return -1;
		else if (fc_first)
		{
			fc_result = exprTypmod(fc_expr);
			fc_first = false;
		}
		else
		{
			/* 一旦看到不匹配的 typmod，就回退到 -1 */
			if (fc_result != exprTypmod(fc_expr))
				return -1;
		}
	}

	return fc_result;
}

/*
 * check_generic_type_consistency()
 *		实际参数是否可能与多态函数兼容？
 *
 * 参数一致性规则是：
 *
 * 1) 所有声明为 ANYELEMENT 的参数必须具有相同的数据类型。
 * 2) 所有声明为 ANYARRAY 的参数必须具有相同的数据类型，
 *	  该数据类型必须是 varlena 数组类型。
 * 3) 所有声明为 ANYRANGE 的参数必须是相同的范围类型。
 *	  同样，所有声明为 ANYMULTIRANGE 的参数必须是相同的
 *	  多重范围类型；如果这两者都出现，则 ANYRANGE 类型
 *	  必须是 ANYMULTIRANGE 类型的元素类型。
 * 4) 如果有多个这些多态类型的参数，
 *	  数组元素类型和/或范围子类型必须彼此相同，并且与
 *	  ANYELEMENT 类型相同。
 * 5) ANYENUM 与 ANYELEMENT 相同，但如果使用它（单独或与普通
 *	  ANYELEMENT 组合），我们增加额外条件：ANYELEMENT 类型
 *	  必须是枚举类型。
 * 6) ANYNONARRAY 与 ANYELEMENT 相同，但如果使用它，
 *	  我们增加额外条件：ANYELEMENT 类型不得是数组。
 *	  （如果与 ANYARRAY 或 ANYENUM 组合使用，则此条没有效果，
 *	  但如果不使用则为额外限制。）
 * 7) 所有声明为 ANYCOMPATIBLE 的参数必须可以隐式转换
 *	  为一个公共超类型（根据 select_common_type 的规则选择）。
 *	  ANYCOMPATIBLENONARRAY 的作用与 ANYCOMPATIBLE 类似，但
 *	  还要求公共超类型不能是数组。如果存在 ANYCOMPATIBLEARRAY
 *	  或 ANYCOMPATIBLERANGE 或 ANYCOMPATIBLEMULTIRANGE 参数，其元素
 *	  类型或子类型将在选择公共超类型时考虑。
 * 8) ANYCOMPATIBLEARRAY 参数的解析类型将是公共超类型的
 *	  数组类型（这可能与任何原始数组的数组类型不同）。
 * 9) 所有 ANYCOMPATIBLERANGE 参数必须是完全相同的范围类型
 *	  （在域扁平化后），因为我们没有优先规则可以让我们选择
 *	  一个而不是另一个。此外，该范围的子类型必须完全匹配
 *	  规则 7 选择的公共超类型。
 * 10) 所有 ANYCOMPATIBLEMULTIRANGE 参数必须是完全相同的多重范围
 *	  类型（在域扁平化后），因为我们没有优先规则可以让我们
 *	  选择一个而不是另一个。此外，如果 ANYCOMPATIBLERANGE
 *	  也出现，该范围类型必须是多重范围的元素类型；
 *	  否则，多重范围的范围的子类型必须完全匹配
 *	  规则 7 选择的公共超类型。
 *
 * 数组上的域匹配 ANYARRAY，并立即被扁平化到其基础类型。
 * （因此，例如，如果一个 ANYARRAY 参数是 int4[] 的域，
 * 另一个参数只是 int4[]，我们将认为这是匹配的。）同样
 * 注意，这种域 *不* 匹配 ANYNONARRAY。ANYCOMPATIBLEARRAY 和
 * ANYCOMPATIBLENONARRAY 也同样如此。
 *
 * 同样，范围上的域匹配 ANYRANGE 或 ANYCOMPATIBLERANGE，
 * 并立即被扁平化到其基础类型。类似地，多重范围上的域
 * 匹配 ANYMULTIRANGE 或 ANYCOMPATIBLEMULTIRANGE，并立即
 * 被扁平化到其基础类型。
 *
 * 请注意，当前域不被认为匹配 ANYENUM，
 * 即使其基础类型会匹配。
 *
 * 如果我们对任何多态参数有 UNKNOWN 输入（即，未类型化的字面量），
 * 假设这是可以的。
 *
 * 如果违反了规则，我们在此处不报告，而是直接返回 false。
 */
bool check_generic_type_consistency(const Oid *fc_actual_arg_types,
							   const Oid *fc_declared_arg_types,
							   int fc_nargs)
{
	Oid			fc_elem_typeid = InvalidOid;
	Oid			fc_array_typeid = InvalidOid;
	Oid			fc_range_typeid = InvalidOid;
	Oid			fc_multirange_typeid = InvalidOid;
	Oid			fc_anycompatible_range_typeid = InvalidOid;
	Oid			fc_anycompatible_range_typelem = InvalidOid;
	Oid			fc_anycompatible_multirange_typeid = InvalidOid;
	Oid			fc_anycompatible_multirange_typelem = InvalidOid;
	Oid			fc_range_typelem = InvalidOid;
	bool		fc_have_anynonarray = false;
	bool		fc_have_anyenum = false;
	bool		fc_have_anycompatible_nonarray = false;
	int			fc_n_anycompatible_args = 0;
	Oid			fc_anycompatible_actual_types[FUNC_MAX_ARGS];

	/*
	 * 遍历参数以查看是否有任何多态参数。
	 * 如果有，则要求实际类型一致。
	 */
	Assert(fc_nargs <= FUNC_MAX_ARGS);
	for (int fc_j = 0; fc_j < fc_nargs; fc_j++)
	{
		Oid			fc_decl_type = fc_declared_arg_types[fc_j];
		Oid			fc_actual_type = fc_actual_arg_types[fc_j];

		if (fc_decl_type == ANYELEMENTOID ||
			fc_decl_type == ANYNONARRAYOID ||
			fc_decl_type == ANYENUMOID)
		{
			if (fc_decl_type == ANYNONARRAYOID)
				fc_have_anynonarray = true;
			else if (fc_decl_type == ANYENUMOID)
				fc_have_anyenum = true;
			if (fc_actual_type == UNKNOWNOID)
				continue;
			if (OidIsValid(fc_elem_typeid) && fc_actual_type != fc_elem_typeid)
				return false;
			fc_elem_typeid = fc_actual_type;
		}
		else if (fc_decl_type == ANYARRAYOID)
		{
			if (fc_actual_type == UNKNOWNOID)
				continue;
			fc_actual_type = getBaseType(fc_actual_type); /* 扁平化域 */
			if (OidIsValid(fc_array_typeid) && fc_actual_type != fc_array_typeid)
				return false;
			fc_array_typeid = fc_actual_type;
		}
		else if (fc_decl_type == ANYRANGEOID)
		{
			if (fc_actual_type == UNKNOWNOID)
				continue;
			fc_actual_type = getBaseType(fc_actual_type); /* 扁平化域 */
			if (OidIsValid(fc_range_typeid) && fc_actual_type != fc_range_typeid)
				return false;
			fc_range_typeid = fc_actual_type;
		}
		else if (fc_decl_type == ANYMULTIRANGEOID)
		{
			if (fc_actual_type == UNKNOWNOID)
				continue;
			fc_actual_type = getBaseType(fc_actual_type); /* 扁平化域 */
			if (OidIsValid(fc_multirange_typeid) && fc_actual_type != fc_multirange_typeid)
				return false;
			fc_multirange_typeid = fc_actual_type;
		}
		else if (fc_decl_type == ANYCOMPATIBLEOID ||
				 fc_decl_type == ANYCOMPATIBLENONARRAYOID)
		{
			if (fc_decl_type == ANYCOMPATIBLENONARRAYOID)
				fc_have_anycompatible_nonarray = true;
			if (fc_actual_type == UNKNOWNOID)
				continue;
			/* 收集真实类型的非未知兼容参数 */
			fc_anycompatible_actual_types[fc_n_anycompatible_args++] = fc_actual_type;
		}
		else if (fc_decl_type == ANYCOMPATIBLEARRAYOID)
		{
			Oid			fc_elem_type;

			if (fc_actual_type == UNKNOWNOID)
				continue;
			fc_actual_type = getBaseType(fc_actual_type); /* 扁平化域 */
			fc_elem_type = get_element_type(fc_actual_type);
			if (!OidIsValid(fc_elem_type))
				return false;	/* 不是数组 */
			/* 收集用于公共超类型选择的元素类型 */
			fc_anycompatible_actual_types[fc_n_anycompatible_args++] = fc_elem_type;
		}
		else if (fc_decl_type == ANYCOMPATIBLERANGEOID)
		{
			if (fc_actual_type == UNKNOWNOID)
				continue;
			fc_actual_type = getBaseType(fc_actual_type); /* 扁平化域 */
			if (OidIsValid(fc_anycompatible_range_typeid))
			{
				/* 所有 ANYCOMPATIBLERANGE 参数必须是相同类型 */
				if (fc_anycompatible_range_typeid != fc_actual_type)
					return false;
			}
			else
			{
				fc_anycompatible_range_typeid = fc_actual_type;
				fc_anycompatible_range_typelem = get_range_subtype(fc_actual_type);
				if (!OidIsValid(fc_anycompatible_range_typelem))
					return false;	
/* 不是一个范围类型 */
				/* 收集通用超类型选择的子类型 */
				fc_anycompatible_actual_types[fc_n_anycompatible_args++] = fc_anycompatible_range_typelem;
			}
		}
		else if (fc_decl_type == ANYCOMPATIBLEMULTIRANGEOID)
		{
			if (fc_actual_type == UNKNOWNOID)
				continue;
			fc_actual_type = getBaseType(fc_actual_type); /* 扁平化域 */
			if (OidIsValid(fc_anycompatible_multirange_typeid))
			{
				/* 所有 ANYCOMPATIBLEMULTIRANGE 参数必须是相同类型 */
				if (fc_anycompatible_multirange_typeid != fc_actual_type)
					return false;
			}
			else
			{
				fc_anycompatible_multirange_typeid = fc_actual_type;
				fc_anycompatible_multirange_typelem = get_multirange_range(fc_actual_type);
				if (!OidIsValid(fc_anycompatible_multirange_typelem))
					return false;	/* 不是一个多范围类型 */
				/* 我们将在下面考虑子类型 */
			}
		}
	}

	/* 如果有数组类型，则根据数组类型获取元素类型 */
	if (OidIsValid(fc_array_typeid))
	{
		if (fc_array_typeid == ANYARRAYOID)
		{
			/*
			 * 匹配 ANYARRAY 输入到 ANYARRAY 
			 * 参数的特殊情况：暂时允许它。 enforce_generic_type_consistency() 
			 * 可能会在稍后抱怨，具体取决于其他多态参数或结果的存在，但它会提供比“函数不存在”更不令人意外的错误信息。
			 *
			 * （如果你想改变这一点，请注意 can_coerce_type 将
			 * 将这种情况视为匹配，因此我们甚至可能不会到达这里。）
			 */
		}
		else
		{
			Oid			fc_array_typelem;

			fc_array_typelem = get_element_type(fc_array_typeid);
			if (!OidIsValid(fc_array_typelem))
				return false;	/* 应该是一个数组，但不是 */

			if (!OidIsValid(fc_elem_typeid))
			{
				/*
				 * 如果我们还没有元素类型，请使用我们刚刚获得的
				 */
				fc_elem_typeid = fc_array_typelem;
			}
			else if (fc_array_typelem != fc_elem_typeid)
			{
				/* 否则，它们最好匹配 */
				return false;
			}
		}
	}

	/* 从多范围类型推断范围类型，或检查它们是否一致 */
	if (OidIsValid(fc_multirange_typeid))
	{
		Oid			fc_multirange_typelem;

		fc_multirange_typelem = get_multirange_range(fc_multirange_typeid);
		if (!OidIsValid(fc_multirange_typelem))
			return false;		/* 应该是一个多范围，但不是 */

		if (!OidIsValid(fc_range_typeid))
		{
			/* 如果我们还没有范围类型，请使用我们刚刚获得的 */
			fc_range_typeid = fc_multirange_typelem;
			fc_range_typelem = get_range_subtype(fc_multirange_typelem);
			if (!OidIsValid(fc_range_typelem))
				return false;	/* 应该是一个范围，但不是 */
		}
		else if (fc_multirange_typelem != fc_range_typeid)
		{
			/* 否则，它们最好匹配 */
			return false;
		}
	}

	/* 如果有范围类型，则根据范围类型获取元素类型 */
	if (OidIsValid(fc_range_typeid))
	{
		fc_range_typelem = get_range_subtype(fc_range_typeid);
		if (!OidIsValid(fc_range_typelem))
			return false;		/* 应该是一个范围，但不是 */

		if (!OidIsValid(fc_elem_typeid))
		{
			/*
			 * 如果我们还没有元素类型，请使用我们刚刚获得的
			 */
			fc_elem_typeid = fc_range_typelem;
		}
		else if (fc_range_typelem != fc_elem_typeid)
		{
			/* 否则，它们最好匹配 */
			return false;
		}
	}

	if (fc_have_anynonarray)
	{
		/* 要求元素类型不能是数组或数组上的域 */
		if (type_is_array_domain(fc_elem_typeid))
			return false;
	}

	if (fc_have_anyenum)
	{
		/* 要求元素类型为枚举 */
		if (!type_is_enum(fc_elem_typeid))
			return false;
	}

	/* 从多范围类型推断范围类型，或检查它们是否一致 */
	if (OidIsValid(fc_anycompatible_multirange_typeid))
	{
		if (OidIsValid(fc_anycompatible_range_typeid))
		{
			if (fc_anycompatible_multirange_typelem !=
				fc_anycompatible_range_typeid)
				return false;
		}
		else
		{
			fc_anycompatible_range_typeid = fc_anycompatible_multirange_typelem;
			fc_anycompatible_range_typelem = get_range_subtype(fc_anycompatible_range_typeid);
			if (!OidIsValid(fc_anycompatible_range_typelem))
				return false;	
/* 不是一个范围类型 */
			/* 收集通用超类型选择的子类型 */
			fc_anycompatible_actual_types[fc_n_anycompatible_args++] =
				fc_anycompatible_range_typelem;
		}
	}

	/* 检查 ANYCOMPATIBLE 系列参数的匹配，如果有的话 */
	if (fc_n_anycompatible_args > 0)
	{
		Oid			fc_anycompatible_typeid;

		fc_anycompatible_typeid =
			fc_select_common_type_from_oids(fc_n_anycompatible_args,
										 fc_anycompatible_actual_types,
										 true);

		if (!OidIsValid(fc_anycompatible_typeid))
			return false;		/* 显然没有共同的超类型 */

		/* 我们必须验证所选类型实际上是可行的 */
		if (!fc_verify_common_type_from_oids(fc_anycompatible_typeid,
										  fc_n_anycompatible_args,
										  fc_anycompatible_actual_types))
			return false;

		if (fc_have_anycompatible_nonarray)
		{
			/*
			 * 要求 anycompatible 类型不能是数组或基于数组的域
			 */
			if (type_is_array_domain(fc_anycompatible_typeid))
				return false;
		}

		/*
		 * anycompatible 类型必须完全匹配范围元素类型，
		 * 如果我们能够识别出一个。这也检查 anycompatiblemultirange 的兼容性，因为它也在上面设置
		 * anycompatible_range_typelem。
		 */
		if (OidIsValid(fc_anycompatible_range_typelem) &&
			fc_anycompatible_range_typelem != fc_anycompatible_typeid)
			return false;
	}

	/* 看起来有效 */
	return true;
}


/*
 * enforce_generic_type_consistency()
 * 确保多态函数合法可调用，并推导实际参数和返回值类型。
 *
 * 如果在函数的参数或返回类型中使用了任何多态伪类型，我们确保实际数据类型之间是一致的。参数一致性规则在上文中展示了 check_generic_type_consistency()。
 *
 * 如果我们对于任何多态参数有 UNKNOWN 输入（即无类型字面量），我们会尝试推导它应该具有的实际类型。如果成功，我们会修改 declared_arg_types[] 的该位置，以便 make_fn_arguments 将字面量强制转换为正确的类型。
 *
 * 如果我们有属于 ANYCOMPATIBLE 家族的多态参数，我们同样会修改 declared_arg_types[] 中的条目以显示解析出的公共超类型，以便 make_fn_arguments 将实际参数强制转换为正确的类型。
 *
 * 如果函数的返回类型被声明为多态类型，并且至少有一个多态参数类型，则规则适用（可能会更改它）：
 *
 * 1) 如果返回类型是 ANYELEMENT，并且任何参数是 ANYELEMENT，则将参数的实际类型用作函数的返回类型。
 * 2) 如果返回类型是 ANYARRAY，并且任何参数是 ANYARRAY，则将参数的实际类型用作函数的返回类型。
 * 3) 类似地，如果返回类型是 ANYRANGE 或 ANYMULTIRANGE，并且任何参数是 ANYRANGE 或 ANYMULTIRANGE，则将该参数的实际类型（或相应的范围或多重范围类型）用作函数的返回类型。
 * 4) 否则，如果返回类型是 ANYELEMENT 或 ANYARRAY，并且至少有一个 ANYELEMENT、ANYARRAY、ANYRANGE 或 ANYMULTIRANGE 输入，则从这些输入推导返回类型，或者如果无法推导，则抛出错误。
 * 5) 否则，如果返回类型是 ANYRANGE 或 ANYMULTIRANGE，则抛出错误。（如果参数中不包括 ANYRANGE 或 ANYMULTIRANGE，则无法选择特定的范围类型。）
 * 6) ANYENUM 的处理方式与 ANYELEMENT 相同，唯一的例外是如果它被使用（单独或与普通 ANYELEMENT 组合），我们增加额外条件：ANYELEMENT 类型必须是枚举。
 * 7) ANYNONARRAY 的处理方式与 ANYELEMENT 相同，唯一的例外是如果它被使用，我们增加额外条件：ANYELEMENT 类型必须不是数组。（如果与 ANYARRAY 或 ANYENUM 组合使用，这是一个无操作，但如果不是，则是额外限制。）
 * 8) ANYCOMPATIBLE、ANYCOMPATIBLEARRAY 和 ANYCOMPATIBLENONARRAY 通过解析这些参数（或其元素类型）的公共超类型来处理，然后将所有这些参数强制转换为公共超类型，或者对于 ANYCOMPATIBLEARRAY 将数组类型强制转换为公共超类型。
 * 9) 对于 ANYCOMPATIBLERANGE 和 ANYCOMPATIBLEMULTIRANGE，必须至少有一个与这些参数匹配的非 UNKNOWN 输入，并且所有此类输入必须是相同的范围类型（或其多重范围类型，视情况而定），因为无法从非范围类型推导范围类型。此外，选择 ANYCOMPATIBLE 等的公共超类型时要包括范围类型的子类型，并且必须与该公共超类型完全匹配。
 *
 * 数组或范围上的域分别匹配 ANYARRAY 或 ANYRANGE 参数，并立即扁平化为其基本类型。（特别是，如果返回类型也是 ANYARRAY 或 ANYRANGE，我们将其设置为基本类型而不是域类型。）对于 ANYMULTIRANGE、ANYCOMPATIBLEARRAY、ANYCOMPATIBLERANGE 和 ANYCOMPATIBLEMULTIRANGE 也是如此。
 *
 * 当 allow_poly 为 false 时，我们不期望任何实际参数类型是多态的，我们也不应该返回多态的结果类型。当 allow_poly 为 true 时，拥有多态“实际”参数类型是可以的，我们可以将匹配的多态类型作为结果返回。（目前此情况仅用于检查聚合声明与基础 transfn 的兼容性。）
 *
 * 一个特例是，即使 allow_poly 为 false，我们也可能会看到 ANYARRAY 作为实际参数类型（这仅仅是因为 pg_statistic 中有列在目录中显示为任何数组）。我们允许这与声明的 ANYARRAY 参数匹配，但前提是没有其他多态参数需要与之匹配，并且不需要确定元素类型以推导结果类型。请注意，这意味着如果应用于 pg_statistic 列，接收 ANYARRAY 的函数最好表现正常；它们不能仅假设连续输入都是相同的实际元素类型。对于 ANYCOMPATIBLEARRAY 没有类似的逻辑；因为没有那种类型的目录列，因此我们不会将其视为输入。我们可以考虑将实际的 ANYARRAY 输入匹配到 ANYCOMPATIBLEARRAY 参数，但目前这似乎也没有意义，因为除非还有至少一个其他 ANYCOMPATIBLE 家族参数或结果，否则使用 ANYCOMPATIBLEARRAY 没有价值。
 *
 * 此外，如果没有声明为多态类型的参数，我们将返回未修改的返回类型，即使它是多态的。这在用户声明的函数中不应发生，因为 CREATE FUNCTION 防止了这种情况。但对于一些内置函数，例如 array_in()，确实发生了这种情况。
 */ 
Oid enforce_generic_type_consistency(const Oid *fc_actual_arg_types,
								 Oid *fc_declared_arg_types,
								 int fc_nargs,
								 Oid fc_rettype,
								 bool fc_allow_poly)
{
	bool		fc_have_poly_anycompatible = false;
	bool		fc_have_poly_unknowns = false;
	Oid			fc_elem_typeid = InvalidOid;
	Oid			fc_array_typeid = InvalidOid;
	Oid			fc_range_typeid = InvalidOid;
	Oid			fc_multirange_typeid = InvalidOid;
	Oid			fc_anycompatible_typeid = InvalidOid;
	Oid			fc_anycompatible_array_typeid = InvalidOid;
	Oid			fc_anycompatible_range_typeid = InvalidOid;
	Oid			fc_anycompatible_range_typelem = InvalidOid;
	Oid			fc_anycompatible_multirange_typeid = InvalidOid;
	Oid			fc_anycompatible_multirange_typelem = InvalidOid;
	bool		fc_have_anynonarray = (fc_rettype == ANYNONARRAYOID);
	bool		fc_have_anyenum = (fc_rettype == ANYENUMOID);
	bool		fc_have_anymultirange = (fc_rettype == ANYMULTIRANGEOID);
	bool		fc_have_anycompatible_nonarray = (fc_rettype == ANYCOMPATIBLENONARRAYOID);
	bool		fc_have_anycompatible_array = (fc_rettype == ANYCOMPATIBLEARRAYOID);
	bool		fc_have_anycompatible_range = (fc_rettype == ANYCOMPATIBLERANGEOID);
	bool		fc_have_anycompatible_multirange = (fc_rettype == ANYCOMPATIBLEMULTIRANGEOID);
	int			fc_n_poly_args = 0;	
/* 这计算所有 family-1 参数 */
	int			fc_n_anycompatible_args = 0;	/* 这只计算非未知值 */
	Oid			fc_anycompatible_actual_types[FUNC_MAX_ARGS];

	/*
	 * 遍历参数以查看是否有任何多态参数。
	 * 如果有，则要求实际类型一致。
	 */
	Assert(fc_nargs <= FUNC_MAX_ARGS);
	for (int fc_j = 0; fc_j < fc_nargs; fc_j++)
	{
		Oid			fc_decl_type = fc_declared_arg_types[fc_j];
		Oid			fc_actual_type = fc_actual_arg_types[fc_j];

		if (fc_decl_type == ANYELEMENTOID ||
			fc_decl_type == ANYNONARRAYOID ||
			fc_decl_type == ANYENUMOID)
		{
			fc_n_poly_args++;
			if (fc_decl_type == ANYNONARRAYOID)
				fc_have_anynonarray = true;
			else if (fc_decl_type == ANYENUMOID)
				fc_have_anyenum = true;
			if (fc_actual_type == UNKNOWNOID)
			{
				fc_have_poly_unknowns = true;
				continue;
			}
			if (fc_allow_poly && fc_decl_type == fc_actual_type)
				continue;		/* 这里没有新信息 */
			if (OidIsValid(fc_elem_typeid) && fc_actual_type != fc_elem_typeid)
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("arguments declared \"%s\" are not all alike", "anyelement"),
						 errdetail("%s versus %s",
								   format_type_be(fc_elem_typeid),
								   format_type_be(fc_actual_type))));
			fc_elem_typeid = fc_actual_type;
		}
		else if (fc_decl_type == ANYARRAYOID)
		{
			fc_n_poly_args++;
			if (fc_actual_type == UNKNOWNOID)
			{
				fc_have_poly_unknowns = true;
				continue;
			}
			if (fc_allow_poly && fc_decl_type == fc_actual_type)
				continue;		/* 这里没有新信息 */
			fc_actual_type = getBaseType(fc_actual_type); /* 扁平化域 */
			if (OidIsValid(fc_array_typeid) && fc_actual_type != fc_array_typeid)
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("arguments declared \"%s\" are not all alike", "anyarray"),
						 errdetail("%s versus %s",
								   format_type_be(fc_array_typeid),
								   format_type_be(fc_actual_type))));
			fc_array_typeid = fc_actual_type;
		}
		else if (fc_decl_type == ANYRANGEOID)
		{
			fc_n_poly_args++;
			if (fc_actual_type == UNKNOWNOID)
			{
				fc_have_poly_unknowns = true;
				continue;
			}
			if (fc_allow_poly && fc_decl_type == fc_actual_type)
				continue;		/* 这里没有新信息 */
			fc_actual_type = getBaseType(fc_actual_type); /* 扁平化域 */
			if (OidIsValid(fc_range_typeid) && fc_actual_type != fc_range_typeid)
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("arguments declared \"%s\" are not all alike", "anyrange"),
						 errdetail("%s versus %s",
								   format_type_be(fc_range_typeid),
								   format_type_be(fc_actual_type))));
			fc_range_typeid = fc_actual_type;
		}
		else if (fc_decl_type == ANYMULTIRANGEOID)
		{
			fc_n_poly_args++;
			fc_have_anymultirange = true;
			if (fc_actual_type == UNKNOWNOID)
			{
				fc_have_poly_unknowns = true;
				continue;
			}
			if (fc_allow_poly && fc_decl_type == fc_actual_type)
				continue;		/* 这里没有新信息 */
			fc_actual_type = getBaseType(fc_actual_type); /* 扁平化域 */
			if (OidIsValid(fc_multirange_typeid) && fc_actual_type != fc_multirange_typeid)
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("arguments declared \"%s\" are not all alike", "anymultirange"),
						 errdetail("%s versus %s",
								   format_type_be(fc_multirange_typeid),
								   format_type_be(fc_actual_type))));
			fc_multirange_typeid = fc_actual_type;
		}
		else if (fc_decl_type == ANYCOMPATIBLEOID ||
				 fc_decl_type == ANYCOMPATIBLENONARRAYOID)
		{
			fc_have_poly_anycompatible = true;
			if (fc_decl_type == ANYCOMPATIBLENONARRAYOID)
				fc_have_anycompatible_nonarray = true;
			if (fc_actual_type == UNKNOWNOID)
				continue;
			if (fc_allow_poly && fc_decl_type == fc_actual_type)
				continue;		/* 这里没有新信息 */
			/* 收集真实类型的非未知兼容参数 */
			fc_anycompatible_actual_types[fc_n_anycompatible_args++] = fc_actual_type;
		}
		else if (fc_decl_type == ANYCOMPATIBLEARRAYOID)
		{
			Oid			fc_anycompatible_elem_type;

			fc_have_poly_anycompatible = true;
			fc_have_anycompatible_array = true;
			if (fc_actual_type == UNKNOWNOID)
				continue;
			if (fc_allow_poly && fc_decl_type == fc_actual_type)
				continue;		/* 这里没有新信息 */
			fc_actual_type = getBaseType(fc_actual_type); /* 扁平化域 */
			fc_anycompatible_elem_type = get_element_type(fc_actual_type);
			if (!OidIsValid(fc_anycompatible_elem_type))
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("argument declared %s is not an array but type %s",
								"anycompatiblearray",
								format_type_be(fc_actual_type))));
			/* 收集用于公共超类型选择的元素类型 */
			fc_anycompatible_actual_types[fc_n_anycompatible_args++] = fc_anycompatible_elem_type;
		}
		else if (fc_decl_type == ANYCOMPATIBLERANGEOID)
		{
			fc_have_poly_anycompatible = true;
			fc_have_anycompatible_range = true;
			if (fc_actual_type == UNKNOWNOID)
				continue;
			if (fc_allow_poly && fc_decl_type == fc_actual_type)
				continue;		/* 这里没有新信息 */
			fc_actual_type = getBaseType(fc_actual_type); /* 扁平化域 */
			if (OidIsValid(fc_anycompatible_range_typeid))
			{
				/* 所有 ANYCOMPATIBLERANGE 参数必须是相同类型 */
				if (fc_anycompatible_range_typeid != fc_actual_type)
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("arguments declared \"%s\" are not all alike", "anycompatiblerange"),
							 errdetail("%s versus %s",
									   format_type_be(fc_anycompatible_range_typeid),
									   format_type_be(fc_actual_type))));
			}
			else
			{
				fc_anycompatible_range_typeid = fc_actual_type;
				fc_anycompatible_range_typelem = get_range_subtype(fc_actual_type);
				if (!OidIsValid(fc_anycompatible_range_typelem))
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("argument declared %s is not a range type but type %s",
									"anycompatiblerange",
									format_type_be(fc_actual_type))));
				/* 收集通用超类型选择的子类型 */
				fc_anycompatible_actual_types[fc_n_anycompatible_args++] = fc_anycompatible_range_typelem;
			}
		}
		else if (fc_decl_type == ANYCOMPATIBLEMULTIRANGEOID)
		{
			fc_have_poly_anycompatible = true;
			fc_have_anycompatible_multirange = true;
			if (fc_actual_type == UNKNOWNOID)
				continue;
			if (fc_allow_poly && fc_decl_type == fc_actual_type)
				continue;		/* 这里没有新信息 */
			fc_actual_type = getBaseType(fc_actual_type); /* 扁平化域 */
			if (OidIsValid(fc_anycompatible_multirange_typeid))
			{
				/* 所有 ANYCOMPATIBLEMULTIRANGE 参数必须是相同类型 */
				if (fc_anycompatible_multirange_typeid != fc_actual_type)
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("arguments declared \"%s\" are not all alike", "anycompatiblemultirange"),
							 errdetail("%s versus %s",
									   format_type_be(fc_anycompatible_multirange_typeid),
									   format_type_be(fc_actual_type))));
			}
			else
			{
				fc_anycompatible_multirange_typeid = fc_actual_type;
				fc_anycompatible_multirange_typelem = get_multirange_range(fc_actual_type);
				if (!OidIsValid(fc_anycompatible_multirange_typelem))
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("argument declared %s is not a multirange type but type %s",
									"anycompatiblemultirange",
									format_type_be(fc_actual_type))));
				/* 我们将在下面考虑子类型 */
			}
		}
	}

	/*
	 * 快速通道：如果没有参数是多态的，返回未修改的返回类型。  如果它是多态的，那就不是我们的工作去解决它。
	 */
	if (fc_n_poly_args == 0 && !fc_have_poly_anycompatible)
		return fc_rettype;

	/* 检查 family-1 多态参数的匹配，如果有的话 */
	if (fc_n_poly_args)
	{
		/* 如果有数组类型，则根据数组类型获取元素类型 */
		if (OidIsValid(fc_array_typeid))
		{
			Oid			fc_array_typelem;

			if (fc_array_typeid == ANYARRAYOID)
			{
				/*
				 * 将 ANYARRAY 输入与 ANYARRAY 参数匹配的特殊情况：仅在没有其他 family-1
				 * 多态参数的情况下允许它（否则我们无法确定数组元素类型是否匹配）并且结果类型不
				 * 需要我们推断特定元素类型。
				 */
				if (fc_n_poly_args != 1 ||
					(fc_rettype != ANYARRAYOID &&
					 IsPolymorphicTypeFamily1(fc_rettype)))
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("cannot determine element type of \"anyarray\" argument")));
				fc_array_typelem = ANYELEMENTOID;
			}
			else
			{
				fc_array_typelem = get_element_type(fc_array_typeid);
				if (!OidIsValid(fc_array_typelem))
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("argument declared %s is not an array but type %s",
									"anyarray", format_type_be(fc_array_typeid))));
			}

			if (!OidIsValid(fc_elem_typeid))
			{
				/*
				 * 如果我们还没有元素类型，请使用我们刚刚获得的
				 */
				fc_elem_typeid = fc_array_typelem;
			}
			else if (fc_array_typelem != fc_elem_typeid)
			{
				/* 否则，它们最好匹配 */
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("argument declared %s is not consistent with argument declared %s",
								"anyarray", "anyelement"),
						 errdetail("%s versus %s",
								   format_type_be(fc_array_typeid),
								   format_type_be(fc_elem_typeid))));
			}
		}

		/* 从多范围类型推导范围类型，或反之 */
		if (OidIsValid(fc_multirange_typeid))
		{
			Oid			fc_multirange_typelem;

			fc_multirange_typelem = get_multirange_range(fc_multirange_typeid);
			if (!OidIsValid(fc_multirange_typelem))
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("argument declared %s is not a multirange type but type %s",
								"anymultirange",
								format_type_be(fc_multirange_typeid))));

			if (!OidIsValid(fc_range_typeid))
			{
				/* 如果我们还没有范围类型，则使用我们刚得到的类型 */
				fc_range_typeid = fc_multirange_typelem;
			}
			else if (fc_multirange_typelem != fc_range_typeid)
			{
				/* 否则，它们最好匹配 */
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("argument declared %s is not consistent with argument declared %s",
								"anymultirange", "anyrange"),
						 errdetail("%s versus %s",
								   format_type_be(fc_multirange_typeid),
								   format_type_be(fc_range_typeid))));
			}
		}
		else if (fc_have_anymultirange && OidIsValid(fc_range_typeid))
		{
			fc_multirange_typeid = get_range_multirange(fc_range_typeid);
			/* 如果那不工作，我们下面会抱怨 */
		}

		/* 如果有范围类型，则根据范围类型获取元素类型 */
		if (OidIsValid(fc_range_typeid))
		{
			Oid			fc_range_typelem;

			fc_range_typelem = get_range_subtype(fc_range_typeid);
			if (!OidIsValid(fc_range_typelem))
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("argument declared %s is not a range type but type %s",
								"anyrange",
								format_type_be(fc_range_typeid))));

			if (!OidIsValid(fc_elem_typeid))
			{
				/*
				 * 如果我们还没有元素类型，请使用我们刚刚获得的
				 */
				fc_elem_typeid = fc_range_typelem;
			}
			else if (fc_range_typelem != fc_elem_typeid)
			{
				/* 否则，它们最好匹配 */
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("argument declared %s is not consistent with argument declared %s",
								"anyrange", "anyelement"),
						 errdetail("%s versus %s",
								   format_type_be(fc_range_typeid),
								   format_type_be(fc_elem_typeid))));
			}
		}

		if (!OidIsValid(fc_elem_typeid))
		{
			if (fc_allow_poly)
			{
				fc_elem_typeid = ANYELEMENTOID;
				fc_array_typeid = ANYARRAYOID;
				fc_range_typeid = ANYRANGEOID;
				fc_multirange_typeid = ANYMULTIRANGEOID;
			}
			else
			{
				/*
				 * 唯一到达这里的方法是所有 family-1 多态参数的输入都是未知的。
				 */
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("could not determine polymorphic type because input has type %s",
								"unknown")));
			}
		}

		if (fc_have_anynonarray && fc_elem_typeid != ANYELEMENTOID)
		{
			/*
			 * 要求元素类型不是数组或数组上的域
			 */
			if (type_is_array_domain(fc_elem_typeid))
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("type matched to anynonarray is an array type: %s",
								format_type_be(fc_elem_typeid))));
		}

		if (fc_have_anyenum && fc_elem_typeid != ANYELEMENTOID)
		{
			/* 要求元素类型为枚举 */
			if (!type_is_enum(fc_elem_typeid))
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("type matched to anyenum is not an enum type: %s",
								format_type_be(fc_elem_typeid))));
		}
	}

	/* 检查 family-2 多态参数的匹配，如果有的话 */
	if (fc_have_poly_anycompatible)
	{
		/* 从多范围类型推导范围类型，或反之 */
		if (OidIsValid(fc_anycompatible_multirange_typeid))
		{
			if (OidIsValid(fc_anycompatible_range_typeid))
			{
				if (fc_anycompatible_multirange_typelem !=
					fc_anycompatible_range_typeid)
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("argument declared %s is not consistent with argument declared %s",
									"anycompatiblemultirange",
									"anycompatiblerange"),
							 errdetail("%s versus %s",
									   format_type_be(fc_anycompatible_multirange_typeid),
									   format_type_be(fc_anycompatible_range_typeid))));
			}
			else
			{
				fc_anycompatible_range_typeid = fc_anycompatible_multirange_typelem;
				fc_anycompatible_range_typelem = get_range_subtype(fc_anycompatible_range_typeid);
				if (!OidIsValid(fc_anycompatible_range_typelem))
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("argument declared %s is not a multirange type but type %s",
									"anycompatiblemultirange",
									format_type_be(fc_anycompatible_multirange_typeid))));
				/* 这使下面的元素类型匹配检查得以启用 */
				fc_have_anycompatible_range = true;
				/* 收集通用超类型选择的子类型 */
				fc_anycompatible_actual_types[fc_n_anycompatible_args++] =
					fc_anycompatible_range_typelem;
			}
		}
		else if (fc_have_anycompatible_multirange &&
				 OidIsValid(fc_anycompatible_range_typeid))
		{
			fc_anycompatible_multirange_typeid = get_range_multirange(fc_anycompatible_range_typeid);
			/* 如果那不工作，我们下面会抱怨 */
		}

		if (fc_n_anycompatible_args > 0)
		{
			fc_anycompatible_typeid =
				fc_select_common_type_from_oids(fc_n_anycompatible_args,
											 fc_anycompatible_actual_types,
											 false);

			/* 我们必须验证所选类型实际上是可行的 */
			if (!fc_verify_common_type_from_oids(fc_anycompatible_typeid,
											  fc_n_anycompatible_args,
											  fc_anycompatible_actual_types))
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("arguments of anycompatible family cannot be cast to a common type")));

			if (fc_have_anycompatible_array)
			{
				fc_anycompatible_array_typeid = get_array_type(fc_anycompatible_typeid);
				if (!OidIsValid(fc_anycompatible_array_typeid))
					ereport(ERROR,
							(errcode(ERRCODE_UNDEFINED_OBJECT),
							 errmsg("could not find array type for data type %s",
									format_type_be(fc_anycompatible_typeid))));
			}

			if (fc_have_anycompatible_range)
			{
				/* 我们无法从其他类型推断范围类型 */
				if (!OidIsValid(fc_anycompatible_range_typeid))
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("could not determine polymorphic type %s because input has type %s",
									"anycompatiblerange", "unknown")));

				/*
				 * 任何兼容类型必须与范围元素类型完全匹配
				 */
				if (fc_anycompatible_range_typelem != fc_anycompatible_typeid)
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("anycompatiblerange type %s does not match anycompatible type %s",
									format_type_be(fc_anycompatible_range_typeid),
									format_type_be(fc_anycompatible_typeid))));
			}

			if (fc_have_anycompatible_multirange)
			{
				/* 我们无法从其他类型推断多范围类型 */
				if (!OidIsValid(fc_anycompatible_multirange_typeid))
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("could not determine polymorphic type %s because input has type %s",
									"anycompatiblemultirange", "unknown")));

				/*
				 * 任何兼容类型必须与多范围元素类型完全匹配
				 */
				if (fc_anycompatible_range_typelem != fc_anycompatible_typeid)
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("anycompatiblemultirange type %s does not match anycompatible type %s",
									format_type_be(fc_anycompatible_multirange_typeid),
									format_type_be(fc_anycompatible_typeid))));
			}

			if (fc_have_anycompatible_nonarray)
			{
				/*
				 * 要求元素类型不是数组或数组上的域
				 */
				if (type_is_array_domain(fc_anycompatible_typeid))
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("type matched to anycompatiblenonarray is an array type: %s",
									format_type_be(fc_anycompatible_typeid))));
			}
		}
		else
		{
			if (fc_allow_poly)
			{
				fc_anycompatible_typeid = ANYCOMPATIBLEOID;
				fc_anycompatible_array_typeid = ANYCOMPATIBLEARRAYOID;
				fc_anycompatible_range_typeid = ANYCOMPATIBLERANGEOID;
				fc_anycompatible_multirange_typeid = ANYCOMPATIBLEMULTIRANGEOID;
			}
			else
			{
				/*
				 * 唯一到达这里的方法是所有 family-2 多态参数的输入都是未知的。  解决为 TEXT，如
				 * select_common_type() 所做的那样。  这并不允许我们使用 TEXTRANGE 或 TEXTMULTIRANGE。
				 */
				fc_anycompatible_typeid = TEXTOID;
				fc_anycompatible_array_typeid = TEXTARRAYOID;
				if (fc_have_anycompatible_range)
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("could not determine polymorphic type %s because input has type %s",
									"anycompatiblerange", "unknown")));
				if (fc_have_anycompatible_multirange)
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("could not determine polymorphic type %s because input has type %s",
									"anycompatiblemultirange", "unknown")));
			}
		}

		/* 用选定类型替换 family-2 多态类型 */
		for (int fc_j = 0; fc_j < fc_nargs; fc_j++)
		{
			Oid			fc_decl_type = fc_declared_arg_types[fc_j];

			if (fc_decl_type == ANYCOMPATIBLEOID ||
				fc_decl_type == ANYCOMPATIBLENONARRAYOID)
				fc_declared_arg_types[fc_j] = fc_anycompatible_typeid;
			else if (fc_decl_type == ANYCOMPATIBLEARRAYOID)
				fc_declared_arg_types[fc_j] = fc_anycompatible_array_typeid;
			else if (fc_decl_type == ANYCOMPATIBLERANGEOID)
				fc_declared_arg_types[fc_j] = fc_anycompatible_range_typeid;
			else if (fc_decl_type == ANYCOMPATIBLEMULTIRANGEOID)
				fc_declared_arg_types[fc_j] = fc_anycompatible_multirange_typeid;
		}
	}

	/*
	 * 如果我们有来自 family-1 多态参数的任何未知输入，重新扫描以分配正确的类型给它们。
	 *
	 * 注意：我们不必考虑与 family-2 多态参数匹配的未知输入，因为我们强制更新了它们
	 * declared_arg_types[] 位置。
	 */
	if (fc_have_poly_unknowns)
	{
		for (int fc_j = 0; fc_j < fc_nargs; fc_j++)
		{
			Oid			fc_decl_type = fc_declared_arg_types[fc_j];
			Oid			fc_actual_type = fc_actual_arg_types[fc_j];

			if (fc_actual_type != UNKNOWNOID)
				continue;

			if (fc_decl_type == ANYELEMENTOID ||
				fc_decl_type == ANYNONARRAYOID ||
				fc_decl_type == ANYENUMOID)
				fc_declared_arg_types[fc_j] = fc_elem_typeid;
			else if (fc_decl_type == ANYARRAYOID)
			{
				if (!OidIsValid(fc_array_typeid))
				{
					fc_array_typeid = get_array_type(fc_elem_typeid);
					if (!OidIsValid(fc_array_typeid))
						ereport(ERROR,
								(errcode(ERRCODE_UNDEFINED_OBJECT),
								 errmsg("could not find array type for data type %s",
										format_type_be(fc_elem_typeid))));
				}
				fc_declared_arg_types[fc_j] = fc_array_typeid;
			}
			else if (fc_decl_type == ANYRANGEOID)
			{
				if (!OidIsValid(fc_range_typeid))
				{
					/* 我们无法从其他类型推断范围类型 */
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("could not determine polymorphic type %s because input has type %s",
									"anyrange", "unknown")));
				}
				fc_declared_arg_types[fc_j] = fc_range_typeid;
			}
			else if (fc_decl_type == ANYMULTIRANGEOID)
			{
				if (!OidIsValid(fc_multirange_typeid))
				{
					/* 我们无法从其他类型推断多范围类型 */
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("could not determine polymorphic type %s because input has type %s",
									"anymultirange", "unknown")));
				}
				fc_declared_arg_types[fc_j] = fc_multirange_typeid;
			}
		}
	}

	/* 如果我们返回 ANYELEMENT，请使用适当的参数类型 */
	if (fc_rettype == ANYELEMENTOID ||
		fc_rettype == ANYNONARRAYOID ||
		fc_rettype == ANYENUMOID)
		return fc_elem_typeid;

	/* 如果我们返回 ANYARRAY，使用适当的参数类型 */
	if (fc_rettype == ANYARRAYOID)
	{
		if (!OidIsValid(fc_array_typeid))
		{
			fc_array_typeid = get_array_type(fc_elem_typeid);
			if (!OidIsValid(fc_array_typeid))
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_OBJECT),
						 errmsg("could not find array type for data type %s",
								format_type_be(fc_elem_typeid))));
		}
		return fc_array_typeid;
	}

	/* 如果我们返回 ANYRANGE，使用适当的参数类型 */
	if (fc_rettype == ANYRANGEOID)
	{
		/* 如果函数签名有效，此错误是不可达的： */
		if (!OidIsValid(fc_range_typeid))
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg_internal("could not determine polymorphic type %s because input has type %s",
									 "anyrange", "unknown")));
		return fc_range_typeid;
	}

	/* 如果我们返回 ANYMULTIRANGE，使用适当的参数类型 */
	if (fc_rettype == ANYMULTIRANGEOID)
	{
		/* 如果函数签名有效，此错误是不可达的： */
		if (!OidIsValid(fc_multirange_typeid))
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg_internal("could not determine polymorphic type %s because input has type %s",
									 "anymultirange", "unknown")));
		return fc_multirange_typeid;
	}

	/* 如果我们返回 ANYCOMPATIBLE，使用适当的类型 */
	if (fc_rettype == ANYCOMPATIBLEOID ||
		fc_rettype == ANYCOMPATIBLENONARRAYOID)
	{
		/* 如果函数签名有效，此错误是不可达的： */
		if (!OidIsValid(fc_anycompatible_typeid))
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg_internal("could not identify anycompatible type")));
		return fc_anycompatible_typeid;
	}

	/* 如果我们返回 ANYCOMPATIBLEARRAY，使用适当的类型 */
	if (fc_rettype == ANYCOMPATIBLEARRAYOID)
	{
		/* 如果函数签名有效，此错误是不可达的： */
		if (!OidIsValid(fc_anycompatible_array_typeid))
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg_internal("could not identify anycompatiblearray type")));
		return fc_anycompatible_array_typeid;
	}

	/* 如果我们返回 ANYCOMPATIBLERANGE，使用适当的参数类型 */
	if (fc_rettype == ANYCOMPATIBLERANGEOID)
	{
		/* 如果函数签名有效，此错误是不可达的： */
		if (!OidIsValid(fc_anycompatible_range_typeid))
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg_internal("could not identify anycompatiblerange type")));
		return fc_anycompatible_range_typeid;
	}

	/* 如果我们返回 ANYCOMPATIBLEMULTIRANGE，使用适当的参数类型 */
	if (fc_rettype == ANYCOMPATIBLEMULTIRANGEOID)
	{
		/* 如果函数签名有效，此错误是不可达的： */
		if (!OidIsValid(fc_anycompatible_multirange_typeid))
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg_internal("could not identify anycompatiblemultirange type")));
		return fc_anycompatible_multirange_typeid;
	}

	/* 我们不返回泛型类型；返回原始返回类型 */
	return fc_rettype;
}

/*
 * check_valid_polymorphic_signature()
 *		提议的函数签名是否符合多态规则的有效性？
 *
 * 如果签名有效（即ret_type不是多态的，或者可以从给定声明的参数类型中推导出来），则返回NULL。
 * 否则，返回一个已分配内存、已翻译的错误详细信息字符串，说明为什么不。
 */
char * check_valid_polymorphic_signature(Oid fc_ret_type,
								  const Oid *fc_declared_arg_types,
								  int fc_nargs)
{
	if (fc_ret_type == ANYRANGEOID || fc_ret_type == ANYMULTIRANGEOID)
	{
		/*
		 * ANYRANGE和ANYMULTIRANGE需要ANYRANGE或ANYMULTIRANGE
		 * 输入，否则我们无法知道使用哪种具有相同元素类型的多个范围类型。
		 */
		for (int fc_i = 0; fc_i < fc_nargs; fc_i++)
		{
			if (fc_declared_arg_types[fc_i] == ANYRANGEOID ||
				fc_declared_arg_types[fc_i] == ANYMULTIRANGEOID)
				return NULL;	/* 好的 */
		}
		return psprintf(_("A result of type %s requires at least one input of type anyrange or anymultirange."),
						format_type_be(fc_ret_type));
	}
	else if (fc_ret_type == ANYCOMPATIBLERANGEOID || fc_ret_type == ANYCOMPATIBLEMULTIRANGEOID)
	{
		/*
		 * ANYCOMPATIBLERANGE和ANYCOMPATIBLEMULTIRANGE需要
		 * ANYCOMPATIBLERANGE或ANYCOMPATIBLEMULTIRANGE输入，否则我们无法
		 * 确定使用哪种具有相同元素类型的多个范围类型。
		 */
		for (int fc_i = 0; fc_i < fc_nargs; fc_i++)
		{
			if (fc_declared_arg_types[fc_i] == ANYCOMPATIBLERANGEOID ||
				fc_declared_arg_types[fc_i] == ANYCOMPATIBLEMULTIRANGEOID)
				return NULL;	/* 好的 */
		}
		return psprintf(_("A result of type %s requires at least one input of type anycompatiblerange or anycompatiblemultirange."),
						format_type_be(fc_ret_type));
	}
	else if (IsPolymorphicTypeFamily1(fc_ret_type))
	{
		/* 否则，任何family-1类型可以从任何其他类型中推导出来 */
		for (int fc_i = 0; fc_i < fc_nargs; fc_i++)
		{
			if (IsPolymorphicTypeFamily1(fc_declared_arg_types[fc_i]))
				return NULL;	/* 好的 */
		}
		/* 保持此列表与IsPolymorphicTypeFamily1同步！ */
		return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange."),
						format_type_be(fc_ret_type));
	}
	else if (IsPolymorphicTypeFamily2(fc_ret_type))
	{
		/* 否则，任何family-2类型可以从任何其他类型中推导出来 */
		for (int fc_i = 0; fc_i < fc_nargs; fc_i++)
		{
			if (IsPolymorphicTypeFamily2(fc_declared_arg_types[fc_i]))
				return NULL;	/* 好的 */
		}
		/* 保持此列表与IsPolymorphicTypeFamily2同步！ */
		return psprintf(_("A result of type %s requires at least one input of type anycompatible, anycompatiblearray, anycompatiblenonarray, anycompatiblerange, or anycompatiblemultirange."),
						format_type_be(fc_ret_type));
	}
	else
		return NULL;			/* 好的，ret_type不是多态的 */
}

/*
 * check_valid_internal_signature()
 *		提议的函数签名是否符合INTERNAL安全规则的有效性？
 *
 * 如果正常，返回NULL；如果ret_type是INTERNAL但声明的arg类型没有，则返回适当的错误消息。
 * （创建这样的函数是不安全的，因为它将允许从SQL直接调用消耗INTERNAL的函数。）返回错误详细信息消息是多余的，因为
 * 只有一种可能性，但我们这样做是为了保持API与
 * check_valid_polymorphic_signature()相似。
 */
char * check_valid_internal_signature(Oid fc_ret_type,
							   const Oid *fc_declared_arg_types,
							   int fc_nargs)
{
	if (fc_ret_type == INTERNALOID)
	{
		for (int fc_i = 0; fc_i < fc_nargs; fc_i++)
		{
			if (fc_declared_arg_types[fc_i] == fc_ret_type)
				return NULL;	/* 好的 */
		}
		return pstrdup(_("A result of type internal requires at least one input of type internal."));
	}
	else
		return NULL;			/* 好的，ret_type不是INTERNAL */
}


/* TypeCategory()
 *		为指定的类型OID分配一个类别。
 *
 * 注意：这不能返回TYPCATEGORY_INVALID。
 */
TYPCATEGORY
TypeCategory(Oid fc_type)
{
	char		fc_typcategory;
	bool		fc_typispreferred;

	get_type_category_preferred(fc_type, &fc_typcategory, &fc_typispreferred);
	Assert(fc_typcategory != TYPCATEGORY_INVALID);
	return (TYPCATEGORY) fc_typcategory;
}


/* IsPreferredType()
 *		检查此类型是否是给定类别的首选类型。
 *
 * 如果类别是TYPCATEGORY_INVALID，则对于任何类别的首选类型，我们将返回true；否则，仅返回该类别的首选类型。
 */
bool IsPreferredType(TYPCATEGORY fc_category, Oid fc_type)
{
	char		fc_typcategory;
	bool		fc_typispreferred;

	get_type_category_preferred(fc_type, &fc_typcategory, &fc_typispreferred);
	if (fc_category == fc_typcategory || fc_category == TYPCATEGORY_INVALID)
		return fc_typispreferred;
	else
		return false;
}


/* IsBinaryCoercible()
 *		检查srctype是否可以强制转换为targettype。
 *
 * 这一概念允许我们作弊并直接交换值，而无需调用转换函数。请注意，
 * 通常，这应该只是一个实现快捷方式。在7.4之前，
 * 这也是用于解决重载函数和运算符的启发式方法，但这基本上是个坏主意。
 *
 * 从7.3开始，二进制强制转换不再硬编码到代码中。
 * 如果存在可以隐式调用的不需要函数的pg_cast条目，我们认为两种类型是二进制可强制转换的。
 * 此外，域总是可以强制转换为其基础类型，但反之则不可以（在另一个方向上，必须在接受值作为合法值之前应用域约束检查）。
 * 我们还需要对各种多态类型进行特例处理。
 *
 * 此函数替代了IsBinaryCompatible()，后者本质上是一个对称测试。由于pg_cast条目不一定是对称的，
 * 操作数的顺序现在变得重要。
 */
bool IsBinaryCoercible(Oid fc_srctype, Oid fc_targettype)
{
	HeapTuple	fc_tuple;
	Form_pg_cast fc_castForm;
	bool		fc_result;

	/* 如果是相同类型，则快速路径 */
	if (fc_srctype == fc_targettype)
		return true;

	/* 任何类型都可以强制转换为ANY或ANYELEMENT或ANYCOMPATIBLE */
	if (fc_targettype == ANYOID || fc_targettype == ANYELEMENTOID ||
		fc_targettype == ANYCOMPATIBLEOID)
		return true;

	/* 如果srctype是域，则减少到其基础类型 */
	if (OidIsValid(fc_srctype))
		fc_srctype = getBaseType(fc_srctype);

	/* 域到基础类型情况的快速路径 */
	if (fc_srctype == fc_targettype)
		return true;

	/* 也接受任何数组类型作为可以强制转换为ANY[COMPATIBLE]ARRAY */
	if (fc_targettype == ANYARRAYOID || fc_targettype == ANYCOMPATIBLEARRAYOID)
		if (type_is_array(fc_srctype))
			return true;

	/* 还接受任何非数组类型作为可强制转换为 ANY[COMPATIBLE]NONARRAY */
	if (fc_targettype == ANYNONARRAYOID || fc_targettype == ANYCOMPATIBLENONARRAYOID)
		if (!type_is_array(fc_srctype))
			return true;

	/* 还接受任何枚举类型作为可强制转换为 ANYENUM */
	if (fc_targettype == ANYENUMOID)
		if (type_is_enum(fc_srctype))
			return true;

	/* 还接受任何范围类型作为可强制转换为 ANY[COMPATIBLE]RANGE */
	if (fc_targettype == ANYRANGEOID || fc_targettype == ANYCOMPATIBLERANGEOID)
		if (type_is_range(fc_srctype))
			return true;

	/* 此外，任何多范围类型可以强制转换为 ANY[COMPATIBLE]MULTIRANGE */
	if (fc_targettype == ANYMULTIRANGEOID || fc_targettype == ANYCOMPATIBLEMULTIRANGEOID)
		if (type_is_multirange(fc_srctype))
			return true;

	/* 还接受任何复合类型作为可强制转换为 RECORD */
	if (fc_targettype == RECORDOID)
		if (ISCOMPLEX(fc_srctype))
			return true;

	/* 还接受任何复合数组类型作为可强制转换为 RECORD[] */
	if (fc_targettype == RECORDARRAYOID)
		if (fc_is_complex_array(fc_srctype))
			return true;

	/* 否则查找pg_cast */
	fc_tuple = SearchSysCache2(CASTSOURCETARGET,
							ObjectIdGetDatum(fc_srctype),
							ObjectIdGetDatum(fc_targettype));
	if (!HeapTupleIsValid(fc_tuple))
		return false;			/* no cast */
	fc_castForm = (Form_pg_cast) GETSTRUCT(fc_tuple);

	fc_result = (fc_castForm->castmethod == COERCION_METHOD_BINARY &&
			  fc_castForm->castcontext == COERCION_CODE_IMPLICIT);

	ReleaseSysCache(fc_tuple);

	return fc_result;
}


/*
 * find_coercion_pathway
 *		查找两种类型之间的强制转换路径。
 *
 * 目前，这仅处理标量类型的情况；不考虑多态类型或复合类型之间的转换。 （也许以后可以把这些纳入？）
 *
 * ccontext 确定可用的转换集合。
 *
 * 可能的结果代码是：
 *	COERCION_PATH_NONE: 找不到任何强制转换路径
 *				*funcid 设置为 InvalidOid
 *	COERCION_PATH_FUNC: 应用返回的强制转换函数 *funcid
 *	COERCION_PATH_RELABELTYPE: 二元兼容转换，无需函数
 *				*funcid 设置为 InvalidOid
 *	COERCION_PATH_ARRAYCOERCE: 需要一个 ArrayCoerceExpr 节点
 *				*funcid 设置为 InvalidOid
 *	COERCION_PATH_COERCEVIAIO: 需要一个 CoerceViaIO 节点
 *				*funcid 设置为 InvalidOid
 *
 * 注意： COERCION_PATH_RELABELTYPE 并不一定意味着进行强制转换不需要工作；如果目标是一个域，则可能需要进行领域约束检查。 如果您希望检查零努力转换，请使用 IsBinaryCoercible()。
 */
CoercionPathType
find_coercion_pathway(Oid fc_targetTypeId, Oid fc_sourceTypeId,
					  CoercionContext fc_ccontext,
					  Oid *fc_funcid)
{
	CoercionPathType fc_result = COERCION_PATH_NONE;
	HeapTuple	fc_tuple;

	*fc_funcid = InvalidOid;

	/* 也许这些类型是域；如果是这样，请查看它们的基本类型 */
	if (OidIsValid(fc_sourceTypeId))
		fc_sourceTypeId = getBaseType(fc_sourceTypeId);
	if (OidIsValid(fc_targetTypeId))
		fc_targetTypeId = getBaseType(fc_targetTypeId);

	/* 域总是可以强制转换到它们的基本类型和从它们的基本类型 */
	if (fc_sourceTypeId == fc_targetTypeId)
		return COERCION_PATH_RELABELTYPE;

	/* 在pg_cast中查找 */
	fc_tuple = SearchSysCache2(CASTSOURCETARGET,
							ObjectIdGetDatum(fc_sourceTypeId),
							ObjectIdGetDatum(fc_targetTypeId));

	if (HeapTupleIsValid(fc_tuple))
	{
		Form_pg_cast fc_castForm = (Form_pg_cast) GETSTRUCT(fc_tuple);
		CoercionContext fc_castcontext;

		/* 将字符值转换为用于castcontext的 CoercionContext 枚举 */
		switch (fc_castForm->castcontext)
		{
			case COERCION_CODE_IMPLICIT:
				fc_castcontext = COERCION_IMPLICIT;
				break;
			case COERCION_CODE_ASSIGNMENT:
				fc_castcontext = COERCION_ASSIGNMENT;
				break;
			case COERCION_CODE_EXPLICIT:
				fc_castcontext = COERCION_EXPLICIT;
				break;
			default:
				elog(ERROR, "unrecognized castcontext: %d",
					 (int) fc_castForm->castcontext);
				fc_castcontext = 0;	/* 保持编译器安静 */
				break;
		}

		/* 依赖于枚举的顺序以获得正确的行为 */
		if (fc_ccontext >= fc_castcontext)
		{
			switch (fc_castForm->castmethod)
			{
				case COERCION_METHOD_FUNCTION:
					fc_result = COERCION_PATH_FUNC;
					*fc_funcid = fc_castForm->castfunc;
					break;
				case COERCION_METHOD_INOUT:
					fc_result = COERCION_PATH_COERCEVIAIO;
					break;
				case COERCION_METHOD_BINARY:
					fc_result = COERCION_PATH_RELABELTYPE;
					break;
				default:
					elog(ERROR, "unrecognized castmethod: %d",
						 (int) fc_castForm->castmethod);
					break;
			}
		}

		ReleaseSysCache(fc_tuple);
	}
	else
	{
		/*
		 * 如果没有 pg_cast 条目，也许我们正在处理一对数组类型。 如果是这样，并且其元素类型具有转换路径，则报告我们可以通过 ArrayCoerceExpr 进行强制转换。
		 *
		 * Hack: 不允许强制转换为oidvector和int2vector，这些类型通常会捕获应该转换到“真实”数组类型的强制转换。 我们希望这些类型被视为许多目的的“真实”数组，但不包括这个。 （此外， ArrayCoerceExpr 并不保证生成一个符合这些数据类型限制的输出，例如为 1 维。）
		 */
		if (fc_targetTypeId != OIDVECTOROID && fc_targetTypeId != INT2VECTOROID)
		{
			Oid			fc_targetElem;
			Oid			fc_sourceElem;

			if ((fc_targetElem = get_element_type(fc_targetTypeId)) != InvalidOid &&
				(fc_sourceElem = get_element_type(fc_sourceTypeId)) != InvalidOid)
			{
				CoercionPathType fc_elempathtype;
				Oid			fc_elemfuncid;

				fc_elempathtype = find_coercion_pathway(fc_targetElem,
													 fc_sourceElem,
													 fc_ccontext,
													 &fc_elemfuncid);
				if (fc_elempathtype != COERCION_PATH_NONE)
				{
					fc_result = COERCION_PATH_ARRAYCOERCE;
				}
			}
		}

		/*
		 * 如果我们仍然没有找到可能性，请考虑使用 I/O 函数进行自动转换。 我们允许将字符串类型的赋值转换和从字符串类型的显式转换以这种方式处理。 （ CoerceViaIO 机制要比这一般得多，但在没有 pg_cast 条目的情况下，我们只希望允许这个。） 坦率地说，最好在两个方向上都坚持显式转换，但这是为了保留许多类型具有隐式（呀！）转换为文本的 pre-8.3 行为的折中。
		 */
		if (fc_result == COERCION_PATH_NONE)
		{
			if (fc_ccontext >= COERCION_ASSIGNMENT &&
				TypeCategory(fc_targetTypeId) == TYPCATEGORY_STRING)
				fc_result = COERCION_PATH_COERCEVIAIO;
			else if (fc_ccontext >= COERCION_EXPLICIT &&
					 TypeCategory(fc_sourceTypeId) == TYPCATEGORY_STRING)
				fc_result = COERCION_PATH_COERCEVIAIO;
		}
	}

	/*
	 * 在解析 PL/pgSQL 赋值时，只要没有正常强制转换可用，就允许使用 I/O 转换。
	 */
	if (fc_result == COERCION_PATH_NONE &&
		fc_ccontext == COERCION_PLPGSQL)
		fc_result = COERCION_PATH_COERCEVIAIO;

	return fc_result;
}


/*
 * find_typmod_coercion_function -- 给定类型是否需要长度强制转换？
 *
 * 如果目标类型拥有从自身到自身的 pg_cast 函数，则必须需要长度强制转换。
 *
 * “bpchar”（即 char(N)）和“numeric”是此类类型的示例。
 *
 * 如果给定类型是一个 varlena 数组类型，则我们不查找与数组类型直接关联的强制转换函数，而是查找与元素类型关联的强制转换函数。 必须使用 ArrayCoerceExpr 节点来应用此类函数。 （注意：目前，在这种情况下返回 funcid 是没有意义的，因为它将在 ArrayCoerceExpr 的 elemexpr 的递归构造中再次被查找。）
 *
 * 我们使用与 find_coercion_pathway 相同的结果枚举，但唯一可能的结果代码是：
 *	COERCION_PATH_NONE: 不需要长度强制转换
 *	COERCION_PATH_FUNC: 应用 *funcid 返回的函数
 *	COERCION_PATH_ARRAYCOERCE: 使用 ArrayCoerceExpr 应用该函数
 */
CoercionPathType
find_typmod_coercion_function(Oid fc_typeId,
							  Oid *fc_funcid)
{
	CoercionPathType fc_result;
	Type		fc_targetType;
	Form_pg_type fc_typeForm;
	HeapTuple	fc_tuple;

	*fc_funcid = InvalidOid;
	fc_result = COERCION_PATH_FUNC;

	fc_targetType = typeidType(fc_typeId);
	fc_typeForm = (Form_pg_type) GETSTRUCT(fc_targetType);

	/* 检查“真实”数组类型 */
	if (IsTrueArrayType(fc_typeForm))
	{
		/* 是的，将我们的注意力转向元素类型 */
		fc_typeId = fc_typeForm->typelem;
		fc_result = COERCION_PATH_ARRAYCOERCE;
	}
	ReleaseSysCache(fc_targetType);

	/* 在pg_cast中查找 */
	fc_tuple = SearchSysCache2(CASTSOURCETARGET,
							ObjectIdGetDatum(fc_typeId),
							ObjectIdGetDatum(fc_typeId));

	if (HeapTupleIsValid(fc_tuple))
	{
		Form_pg_cast fc_castForm = (Form_pg_cast) GETSTRUCT(fc_tuple);

		*fc_funcid = fc_castForm->castfunc;
		ReleaseSysCache(fc_tuple);
	}

	if (!OidIsValid(*fc_funcid))
		fc_result = COERCION_PATH_NONE;

	return fc_result;
}

/*
 * is_complex_array
 *		该类型是否是复合数组？
 *
 * 注意：这不会对 record[] 返回 true；如果需要，请单独检查 RECORDARRAYOID。
 */
static bool fc_is_complex_array(Oid fc_typid)
{
	Oid			fc_elemtype = get_element_type(fc_typid);

	return (OidIsValid(fc_elemtype) && ISCOMPLEX(fc_elemtype));
}


/*
 * 检查 reltypeId 是否是类型 reloftypeId 的带类型表的行类型，或是该行类型的域。 （这在概念上类似于通过 typeInheritsFrom() 检查的子类型关系。）
 */
static bool fc_typeIsOfTypedTable(Oid fc_reltypeId, Oid fc_reloftypeId)
{
	Oid			fc_relid = typeOrDomainTypeRelid(fc_reltypeId);
	bool		fc_result = false;

	if (fc_relid)
	{
		HeapTuple	fc_tp;
		Form_pg_class fc_reltup;

		fc_tp = SearchSysCache1(RELOID, ObjectIdGetDatum(fc_relid));
		if (!HeapTupleIsValid(fc_tp))
			elog(ERROR, "cache lookup failed for relation %u", fc_relid);

		fc_reltup = (Form_pg_class) GETSTRUCT(fc_tp);
		if (fc_reltup->reloftype == fc_reloftypeId)
			fc_result = true;

		ReleaseSysCache(fc_tp);
	}

	return fc_result;
}
